@tearleads/client-sdk 0.1.15 → 0.1.16
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 +2 -0
- package/client/Tearleads.js +1 -0
- package/client/Tearleads.js.map +1 -1
- package/client/index.d.ts +1 -1
- package/client/index.js.map +1 -1
- package/client/organizations/index.d.ts +4 -42
- package/client/organizations/index.js +13 -0
- package/client/organizations/index.js.map +1 -1
- package/client/organizations/organizationGroupNameHydration.js +8 -1
- package/client/organizations/organizationGroupNameHydration.js.map +1 -1
- package/client/organizations/organizationsTypes.d.ts +47 -0
- package/client/organizations/organizationsTypes.js +2 -0
- package/client/organizations/organizationsTypes.js.map +1 -0
- package/client/organizations/principalMutationRecovery.d.ts +16 -0
- package/client/organizations/principalMutationRecovery.js +23 -0
- package/client/organizations/principalMutationRecovery.js.map +1 -0
- package/client/organizations/principalMutationScope.js +7 -2
- package/client/organizations/principalMutationScope.js.map +1 -1
- package/client/principalMutationApi.d.ts +24 -0
- package/client/principalMutationApi.js +108 -0
- package/client/principalMutationApi.js.map +1 -0
- package/client/principalMutationDeadline.js +23 -0
- package/client/principalMutationDeadline.js.map +1 -0
- package/client/workflowRuntime.d.ts +3 -1
- package/client/workflowRuntime.js +44 -5
- package/client/workflowRuntime.js.map +1 -1
- package/data/persistence/principalCurrentAcknowledgementPersistence.d.ts +27 -0
- package/data/persistence/principalCurrentAcknowledgementPersistence.js +109 -0
- package/data/persistence/principalCurrentAcknowledgementPersistence.js.map +1 -0
- package/data/persistence/principalGrantRetirementPersistence.d.ts +3 -3
- package/data/persistence/principalGrantRetirementPersistence.js +4 -1
- package/data/persistence/principalGrantRetirementPersistence.js.map +1 -1
- package/data/persistence/principalKeyEnvelopeCandidates.js +12 -1
- package/data/persistence/principalKeyEnvelopeCandidates.js.map +1 -1
- package/data/persistence/principalMutationJournalPersistence.d.ts +19 -0
- package/data/persistence/principalMutationJournalPersistence.js +53 -0
- package/data/persistence/principalMutationJournalPersistence.js.map +1 -0
- package/data/persistence/verifiedPrincipalPolicyBundle.js +7 -51
- package/data/persistence/verifiedPrincipalPolicyBundle.js.map +1 -1
- package/data/persistence/verifiedPrincipalPolicyCurrent.js +56 -0
- package/data/persistence/verifiedPrincipalPolicyCurrent.js.map +1 -0
- package/data/principals/principalMutationJournal.d.ts +26 -0
- package/data/principals/principalMutationJournal.js +90 -0
- package/data/principals/principalMutationJournal.js.map +1 -0
- package/data/principals/principalMutationJournalRecord.d.ts +8 -0
- package/data/principals/principalMutationJournalRecord.js +25 -0
- package/data/principals/principalMutationJournalRecord.js.map +1 -0
- package/data/sqlite/principalHistoryStageSchema.js +3 -0
- package/data/sqlite/principalHistoryStageSchema.js.map +1 -1
- package/data/sqlite/principalMutationJournalSchema.d.ts +85 -0
- package/data/sqlite/principalMutationJournalSchema.js +13 -0
- package/data/sqlite/principalMutationJournalSchema.js.map +1 -0
- package/data/sqlite/schema.d.ts +83 -0
- package/data/sqlite/schema.js +3 -0
- package/data/sqlite/schema.js.map +1 -1
- package/index.d.ts +3 -3
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/internal/crypto/index.d.ts +1 -1
- package/internal/crypto/index.js +1 -1
- package/internal/crypto/index.js.map +1 -1
- package/internal/crypto/keying/index.d.ts +2 -0
- package/internal/crypto/keying/index.js +2 -0
- package/internal/crypto/keying/index.js.map +1 -1
- package/internal/crypto/keying/principalPolicyCurrent.d.ts +1 -1
- package/internal/crypto/keying/principalPolicyCurrent.js +3 -3
- package/internal/crypto/keying/principalPolicyCurrent.js.map +1 -1
- package/internal/crypto/keying/principalPolicyCurrentEvidence.js +16 -0
- package/internal/crypto/keying/principalPolicyCurrentEvidence.js.map +1 -0
- package/internal/crypto/keying/principalPolicyCurrentMutation.d.ts +10 -0
- package/internal/crypto/keying/principalPolicyCurrentMutation.js +51 -0
- package/internal/crypto/keying/principalPolicyCurrentMutation.js.map +1 -0
- package/internal/crypto/keying/principalPolicyCurrentSuccessor.d.ts +10 -0
- package/internal/crypto/keying/principalPolicyCurrentSuccessor.js +60 -0
- package/internal/crypto/keying/principalPolicyCurrentSuccessor.js.map +1 -0
- package/internal/crypto/keying/principalPolicyHistory.js +4 -1
- package/internal/crypto/keying/principalPolicyHistory.js.map +1 -1
- package/internal/crypto/keying/principalPolicyHistoryReferences.js +1 -1
- package/internal/crypto/keying/principalPolicyHistoryReferences.js.map +1 -1
- package/internal/crypto/keying/principalPolicyHistoryTypes.d.ts +5 -2
- package/internal/crypto/keying/principalPolicyHistoryTypes.js +6 -3
- package/internal/crypto/keying/principalPolicyHistoryTypes.js.map +1 -1
- package/package.json +1 -1
- package/workflows/containers/child/share.js +4 -0
- package/workflows/containers/child/share.js.map +1 -1
- package/workflows/containers/child/sharePrincipalPolicy.d.ts +1 -0
- package/workflows/containers/child/sharePrincipalPolicy.js.map +1 -1
- package/workflows/organizations/acknowledgedDirectoryBindings.js +18 -0
- package/workflows/organizations/acknowledgedDirectoryBindings.js.map +1 -0
- package/workflows/organizations/currentGroupMetadataAuthority.js +32 -0
- package/workflows/organizations/currentGroupMetadataAuthority.js.map +1 -0
- package/workflows/organizations/currentOrganizationAuthority.js +88 -0
- package/workflows/organizations/currentOrganizationAuthority.js.map +1 -0
- package/workflows/organizations/currentOrganizationGroupNames.js +68 -0
- package/workflows/organizations/currentOrganizationGroupNames.js.map +1 -0
- package/workflows/organizations/currentPolicyMutationAcknowledgement.js +65 -0
- package/workflows/organizations/currentPolicyMutationAcknowledgement.js.map +1 -0
- package/workflows/organizations/deleteOrganizationGroup.js +1 -0
- package/workflows/organizations/deleteOrganizationGroup.js.map +1 -1
- package/workflows/organizations/groupCreation.js +1 -0
- package/workflows/organizations/groupCreation.js.map +1 -1
- package/workflows/organizations/groupMetadataAccess.d.ts +2 -2
- package/workflows/organizations/groupMetadataAccess.js +1 -1
- package/workflows/organizations/groupMetadataAccess.js.map +1 -1
- package/workflows/organizations/groupMetadataBinding.js.map +1 -1
- package/workflows/organizations/groupMetadataContainerAuthority.js +2 -47
- package/workflows/organizations/groupMetadataContainerAuthority.js.map +1 -1
- package/workflows/organizations/groupMetadataRootBinding.js +51 -0
- package/workflows/organizations/groupMetadataRootBinding.js.map +1 -0
- package/workflows/organizations/groupMetadataRuntime.js +20 -9
- package/workflows/organizations/groupMetadataRuntime.js.map +1 -1
- package/workflows/organizations/groupMutationAuthorization.js.map +1 -1
- package/workflows/organizations/groupPolicyMutationAcknowledgement.js +9 -75
- package/workflows/organizations/groupPolicyMutationAcknowledgement.js.map +1 -1
- package/workflows/organizations/groupPolicyMutationContext.d.ts +5 -13
- package/workflows/organizations/groupPolicyMutationContext.js +1 -0
- package/workflows/organizations/groupPolicyMutationContext.js.map +1 -1
- package/workflows/organizations/groupPolicyMutationEvidence.d.ts +26 -0
- package/workflows/organizations/groupPolicyMutationEvidence.js +64 -0
- package/workflows/organizations/groupPolicyMutationEvidence.js.map +1 -0
- package/workflows/organizations/groupPolicyMutationHead.js.map +1 -1
- package/workflows/organizations/groupPolicyRequests.js +69 -63
- package/workflows/organizations/groupPolicyRequests.js.map +1 -1
- package/workflows/organizations/index.d.ts +6 -0
- package/workflows/organizations/index.js +5 -0
- package/workflows/organizations/index.js.map +1 -1
- package/workflows/organizations/organizationGroupNames.js +7 -0
- package/workflows/organizations/organizationGroupNames.js.map +1 -1
- package/workflows/organizations/principalCurrentPublicationSeal.js +51 -0
- package/workflows/organizations/principalCurrentPublicationSeal.js.map +1 -0
- package/workflows/organizations/principalMutationJournalLane.js +26 -0
- package/workflows/organizations/principalMutationJournalLane.js.map +1 -0
- package/workflows/organizations/principalMutationJournalManagement.d.ts +26 -0
- package/workflows/organizations/principalMutationJournalManagement.js +104 -0
- package/workflows/organizations/principalMutationJournalManagement.js.map +1 -0
- package/workflows/organizations/principalMutationJournalSession.d.ts +26 -0
- package/workflows/organizations/principalMutationJournalSession.js +85 -0
- package/workflows/organizations/principalMutationJournalSession.js.map +1 -0
- package/workflows/organizations/principalMutationReceipt.js +21 -0
- package/workflows/organizations/principalMutationReceipt.js.map +1 -0
- package/workflows/organizations/principalPolicyReceiptArtifacts.js +74 -0
- package/workflows/organizations/principalPolicyReceiptArtifacts.js.map +1 -0
- package/workflows/organizations/principalPolicyRequest.d.ts +5 -4
- package/workflows/organizations/principalPolicyRequest.js.map +1 -1
- package/workflows/organizations/retainAcknowledgedPrincipalCurrents.d.ts +21 -0
- package/workflows/organizations/retainAcknowledgedPrincipalCurrents.js +155 -0
- package/workflows/organizations/retainAcknowledgedPrincipalCurrents.js.map +1 -0
- package/workflows/principals/principalCurrentRecoveryBatch.js +14 -0
- package/workflows/principals/principalCurrentRecoveryBatch.js.map +1 -0
- package/workflows/principals/principalRecoveryDirectory.d.ts +1 -1
- package/workflows/principals/principalRecoveryDirectory.js +29 -4
- package/workflows/principals/principalRecoveryDirectory.js.map +1 -1
- package/workflows/principals/principalRecoveryDiscoveryPage.js +36 -0
- package/workflows/principals/principalRecoveryDiscoveryPage.js.map +1 -0
- package/workflows/principals/principalRecoveryLocalPreference.js +28 -0
- package/workflows/principals/principalRecoveryLocalPreference.js.map +1 -0
- package/workflows/principals/principalRecoveryMemo.d.ts +16 -0
- package/workflows/principals/principalRecoveryMemo.js +88 -16
- package/workflows/principals/principalRecoveryMemo.js.map +1 -1
- package/workflows/principals/recoverCurrentOrganizationPolicy.js +33 -0
- package/workflows/principals/recoverCurrentOrganizationPolicy.js.map +1 -0
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.d.ts +2 -1
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.js +13 -15
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.js.map +1 -1
- package/workflows/principals/runtimePolicyRecovery.js +14 -8
- package/workflows/principals/runtimePolicyRecovery.js.map +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ApiClient } from "../internal/api-client/index.js";
|
|
2
|
+
import type { SigningKeyPair } from "../internal/crypto/index.js";
|
|
3
|
+
import type { ExecSql } from "../data/sqlite/sqlSchema.js";
|
|
4
|
+
import { type PrincipalMutationRecoveryApi } from "../workflows/organizations/principalMutationJournalManagement.js";
|
|
5
|
+
export interface PrincipalMutationRuntimeScope {
|
|
6
|
+
readonly database: object;
|
|
7
|
+
readonly execSql: ExecSql;
|
|
8
|
+
readonly generation: number;
|
|
9
|
+
readonly identityTrustDomain: string;
|
|
10
|
+
readonly signingFingerprint: string;
|
|
11
|
+
readonly signingKeyPair: SigningKeyPair;
|
|
12
|
+
readonly userId: string;
|
|
13
|
+
}
|
|
14
|
+
export type PrincipalMutationApi = ApiClient & PrincipalMutationRecoveryApi;
|
|
15
|
+
interface PrincipalMutationApiCustodyInput {
|
|
16
|
+
readonly api: ApiClient;
|
|
17
|
+
readonly readScope: () => PrincipalMutationRuntimeScope | null;
|
|
18
|
+
readonly principalMutationTimeoutMs?: number | undefined;
|
|
19
|
+
}
|
|
20
|
+
/** Keep API methods bound to their real receiver and journal only policy writes. */
|
|
21
|
+
export declare function createPrincipalMutationApiCustody(input: PrincipalMutationApiCustodyInput): {
|
|
22
|
+
bind(): PrincipalMutationApi;
|
|
23
|
+
};
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { abandonJournaledPrincipalMutation, discardUnreadableJournaledPrincipalMutation, readJournaledPrincipalMutation, } from "../workflows/organizations/principalMutationJournalManagement.js";
|
|
2
|
+
import { recoverJournaledPrincipalMutation, submitJournaledPrincipalMutation, } from "../workflows/organizations/principalMutationJournalSession.js";
|
|
3
|
+
import { createPrincipalMutationDispatcher } from "./principalMutationDeadline.js";
|
|
4
|
+
function sameScope(current, bound) {
|
|
5
|
+
if (!current || !bound)
|
|
6
|
+
return current === bound;
|
|
7
|
+
return (current.database === bound.database &&
|
|
8
|
+
current.execSql === bound.execSql &&
|
|
9
|
+
current.generation === bound.generation &&
|
|
10
|
+
current.identityTrustDomain === bound.identityTrustDomain &&
|
|
11
|
+
current.signingFingerprint === bound.signingFingerprint &&
|
|
12
|
+
current.signingKeyPair === bound.signingKeyPair &&
|
|
13
|
+
current.userId === bound.userId);
|
|
14
|
+
}
|
|
15
|
+
function mutationContext(input, scope, organizationId) {
|
|
16
|
+
if (!scope)
|
|
17
|
+
throw new Error("Principal mutations require an authenticated signing identity, a trusted API origin and durable local storage");
|
|
18
|
+
return {
|
|
19
|
+
execSql: scope.execSql,
|
|
20
|
+
signingKeyPair: scope.signingKeyPair,
|
|
21
|
+
scope: {
|
|
22
|
+
identityTrustDomain: scope.identityTrustDomain,
|
|
23
|
+
organizationId,
|
|
24
|
+
signingFingerprint: scope.signingFingerprint,
|
|
25
|
+
userId: scope.userId,
|
|
26
|
+
},
|
|
27
|
+
stillCurrent: () => sameScope(input.readScope(), scope),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Keep API methods bound to their real receiver and journal only policy writes. */
|
|
31
|
+
export function createPrincipalMutationApiCustody(input) {
|
|
32
|
+
const dispatch = createPrincipalMutationDispatcher(input.principalMutationTimeoutMs);
|
|
33
|
+
let cached = null;
|
|
34
|
+
return {
|
|
35
|
+
bind() {
|
|
36
|
+
const scope = input.readScope();
|
|
37
|
+
if (cached && sameScope(scope, cached.scope))
|
|
38
|
+
return cached.api;
|
|
39
|
+
const context = (organizationId) => mutationContext(input, scope, organizationId);
|
|
40
|
+
const commit = async (organizationId, groupId, request, options) => submitJournaledPrincipalMutation({
|
|
41
|
+
...context(organizationId),
|
|
42
|
+
mutation: { groupId, request },
|
|
43
|
+
submit: (mutation) => dispatch(options, (dispatchOptions) => input.api.commitOrganizationGroupPolicyResult(organizationId, mutation.groupId, mutation.request, dispatchOptions)),
|
|
44
|
+
});
|
|
45
|
+
const read = async (organizationId) => scope
|
|
46
|
+
? readJournaledPrincipalMutation(context(organizationId))
|
|
47
|
+
: null;
|
|
48
|
+
const abandon = async (organizationId, mutation, acknowledgeUnknownOutcome) => abandonJournaledPrincipalMutation({
|
|
49
|
+
...context(organizationId),
|
|
50
|
+
mutation,
|
|
51
|
+
acknowledgeUnknownOutcome,
|
|
52
|
+
});
|
|
53
|
+
const methods = {
|
|
54
|
+
discardUnreadablePrincipalMutation: async (organizationId, recordId, acknowledgeUnknownOutcome) => discardUnreadableJournaledPrincipalMutation({
|
|
55
|
+
...context(organizationId),
|
|
56
|
+
recordId,
|
|
57
|
+
acknowledgeUnknownOutcome,
|
|
58
|
+
}),
|
|
59
|
+
readPendingPrincipalMutation: read,
|
|
60
|
+
abandonPendingPrincipalMutation: abandon,
|
|
61
|
+
commitOrganizationGroupPolicyResult: commit,
|
|
62
|
+
commitOrganizationGroupPolicy: async (...args) => {
|
|
63
|
+
const result = await commit(...args);
|
|
64
|
+
return result.ok ? result.data : null;
|
|
65
|
+
},
|
|
66
|
+
recoverPendingPrincipalMutation: async (organizationId) => {
|
|
67
|
+
await recoverJournaledPrincipalMutation({
|
|
68
|
+
...context(organizationId),
|
|
69
|
+
submit: (mutation) => dispatch({ reportErrors: false }, (dispatchOptions) => input.api.commitOrganizationGroupPolicyResult(organizationId, mutation.groupId, mutation.request, dispatchOptions)),
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
const api = bindMutationMethods(input.api, methods);
|
|
74
|
+
cached = { scope, api };
|
|
75
|
+
return api;
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function bindMutationMethods(api, methods) {
|
|
80
|
+
const boundMethods = new Map();
|
|
81
|
+
const bound = new Proxy(api, {
|
|
82
|
+
get(target, property) {
|
|
83
|
+
if (Object.hasOwn(methods, property))
|
|
84
|
+
return Reflect.get(methods, property);
|
|
85
|
+
const value = Reflect.get(target, property, target);
|
|
86
|
+
if (typeof value !== "function")
|
|
87
|
+
return value;
|
|
88
|
+
const previous = boundMethods.get(property);
|
|
89
|
+
if (previous?.source === value)
|
|
90
|
+
return previous.bound;
|
|
91
|
+
const bound = value.bind(target);
|
|
92
|
+
boundMethods.set(property, { source: value, bound });
|
|
93
|
+
return bound;
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
// The checked overrides supply the signatures; verify their presence through
|
|
97
|
+
// the reflective boundary before exposing the extended API to callers.
|
|
98
|
+
if (!hasRecoveryMethods(bound))
|
|
99
|
+
throw new Error("Principal mutation recovery methods are unavailable");
|
|
100
|
+
return bound;
|
|
101
|
+
}
|
|
102
|
+
function hasRecoveryMethods(api) {
|
|
103
|
+
return (typeof Reflect.get(api, "readPendingPrincipalMutation") === "function" &&
|
|
104
|
+
typeof Reflect.get(api, "recoverPendingPrincipalMutation") === "function" &&
|
|
105
|
+
typeof Reflect.get(api, "abandonPendingPrincipalMutation") === "function" &&
|
|
106
|
+
typeof Reflect.get(api, "discardUnreadablePrincipalMutation") === "function");
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=principalMutationApi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principalMutationApi.js","sourceRoot":"","sources":["principalMutationApi.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport type { SigningKeyPair } from \"@tearleads/crypto\";\nimport type { ExecSql } from \"../data/sqlite/sqlSchema\";\nimport {\n abandonJournaledPrincipalMutation,\n discardUnreadableJournaledPrincipalMutation,\n type PrincipalMutationRecoveryApi,\n readJournaledPrincipalMutation,\n} from \"../workflows/organizations/principalMutationJournalManagement\";\nimport {\n recoverJournaledPrincipalMutation,\n submitJournaledPrincipalMutation,\n} from \"../workflows/organizations/principalMutationJournalSession\";\nimport { createPrincipalMutationDispatcher } from \"./principalMutationDeadline\";\n\nexport interface PrincipalMutationRuntimeScope {\n readonly database: object;\n readonly execSql: ExecSql;\n readonly generation: number;\n readonly identityTrustDomain: string;\n readonly signingFingerprint: string;\n readonly signingKeyPair: SigningKeyPair;\n readonly userId: string;\n}\n\nfunction sameScope(\n current: PrincipalMutationRuntimeScope | null,\n bound: PrincipalMutationRuntimeScope | null,\n): boolean {\n if (!current || !bound) return current === bound;\n return (\n current.database === bound.database &&\n current.execSql === bound.execSql &&\n current.generation === bound.generation &&\n current.identityTrustDomain === bound.identityTrustDomain &&\n current.signingFingerprint === bound.signingFingerprint &&\n current.signingKeyPair === bound.signingKeyPair &&\n current.userId === bound.userId\n );\n}\n\nexport type PrincipalMutationApi = ApiClient & PrincipalMutationRecoveryApi;\ntype MutationOverrides = Pick<\n ApiClient,\n \"commitOrganizationGroupPolicy\" | \"commitOrganizationGroupPolicyResult\"\n> &\n PrincipalMutationRecoveryApi;\n\ninterface PrincipalMutationApiCustodyInput {\n readonly api: ApiClient;\n readonly readScope: () => PrincipalMutationRuntimeScope | null;\n readonly principalMutationTimeoutMs?: number | undefined;\n}\n\nfunction mutationContext(\n input: PrincipalMutationApiCustodyInput,\n scope: PrincipalMutationRuntimeScope | null,\n organizationId: string,\n) {\n if (!scope)\n throw new Error(\n \"Principal mutations require an authenticated signing identity, a trusted API origin and durable local storage\",\n );\n return {\n execSql: scope.execSql,\n signingKeyPair: scope.signingKeyPair,\n scope: {\n identityTrustDomain: scope.identityTrustDomain,\n organizationId,\n signingFingerprint: scope.signingFingerprint,\n userId: scope.userId,\n },\n stillCurrent: () => sameScope(input.readScope(), scope),\n };\n}\n\n/** Keep API methods bound to their real receiver and journal only policy writes. */\nexport function createPrincipalMutationApiCustody(\n input: PrincipalMutationApiCustodyInput,\n) {\n const dispatch = createPrincipalMutationDispatcher(\n input.principalMutationTimeoutMs,\n );\n let cached: {\n scope: PrincipalMutationRuntimeScope | null;\n api: PrincipalMutationApi;\n } | null = null;\n return {\n bind(): PrincipalMutationApi {\n const scope = input.readScope();\n if (cached && sameScope(scope, cached.scope)) return cached.api;\n const context = (organizationId: string) =>\n mutationContext(input, scope, organizationId);\n const commit: ApiClient[\"commitOrganizationGroupPolicyResult\"] = async (\n organizationId,\n groupId,\n request,\n options,\n ) =>\n submitJournaledPrincipalMutation({\n ...context(organizationId),\n mutation: { groupId, request },\n submit: (mutation) =>\n dispatch(options, (dispatchOptions) =>\n input.api.commitOrganizationGroupPolicyResult(\n organizationId,\n mutation.groupId,\n mutation.request,\n dispatchOptions,\n ),\n ),\n });\n const read: PrincipalMutationRecoveryApi[\"readPendingPrincipalMutation\"] =\n async (organizationId) =>\n scope\n ? readJournaledPrincipalMutation(context(organizationId))\n : null;\n const abandon: PrincipalMutationRecoveryApi[\"abandonPendingPrincipalMutation\"] =\n async (organizationId, mutation, acknowledgeUnknownOutcome) =>\n abandonJournaledPrincipalMutation({\n ...context(organizationId),\n mutation,\n acknowledgeUnknownOutcome,\n });\n const methods = {\n discardUnreadablePrincipalMutation: async (\n organizationId,\n recordId,\n acknowledgeUnknownOutcome,\n ) =>\n discardUnreadableJournaledPrincipalMutation({\n ...context(organizationId),\n recordId,\n acknowledgeUnknownOutcome,\n }),\n readPendingPrincipalMutation: read,\n abandonPendingPrincipalMutation: abandon,\n commitOrganizationGroupPolicyResult: commit,\n commitOrganizationGroupPolicy: async (...args) => {\n const result = await commit(...args);\n return result.ok ? result.data : null;\n },\n recoverPendingPrincipalMutation: async (organizationId) => {\n await recoverJournaledPrincipalMutation({\n ...context(organizationId),\n submit: (mutation) =>\n dispatch({ reportErrors: false }, (dispatchOptions) =>\n input.api.commitOrganizationGroupPolicyResult(\n organizationId,\n mutation.groupId,\n mutation.request,\n dispatchOptions,\n ),\n ),\n });\n },\n } satisfies MutationOverrides;\n const api = bindMutationMethods(input.api, methods);\n cached = { scope, api };\n return api;\n },\n };\n}\n\nfunction bindMutationMethods(\n api: ApiClient,\n methods: MutationOverrides,\n): PrincipalMutationApi {\n const boundMethods = new Map<\n PropertyKey,\n { source: unknown; bound: unknown }\n >();\n const bound = new Proxy(api, {\n get(target, property) {\n if (Object.hasOwn(methods, property))\n return Reflect.get(methods, property);\n const value: unknown = Reflect.get(target, property, target);\n if (typeof value !== \"function\") return value;\n const previous = boundMethods.get(property);\n if (previous?.source === value) return previous.bound;\n const bound = value.bind(target);\n boundMethods.set(property, { source: value, bound });\n return bound;\n },\n });\n // The checked overrides supply the signatures; verify their presence through\n // the reflective boundary before exposing the extended API to callers.\n if (!hasRecoveryMethods(bound))\n throw new Error(\"Principal mutation recovery methods are unavailable\");\n return bound;\n}\n\nfunction hasRecoveryMethods(api: ApiClient): api is PrincipalMutationApi {\n return (\n typeof Reflect.get(api, \"readPendingPrincipalMutation\") === \"function\" &&\n typeof Reflect.get(api, \"recoverPendingPrincipalMutation\") === \"function\" &&\n typeof Reflect.get(api, \"abandonPendingPrincipalMutation\") === \"function\" &&\n typeof Reflect.get(api, \"discardUnreadablePrincipalMutation\") === \"function\"\n );\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** One host-configurable deadline covers dispatch and all preparation exchanges. */
|
|
2
|
+
export function createPrincipalMutationDispatcher(timeoutMs = 60_000) {
|
|
3
|
+
if (!Number.isSafeInteger(timeoutMs) ||
|
|
4
|
+
timeoutMs <= 0 ||
|
|
5
|
+
timeoutMs > 2_147_483_647)
|
|
6
|
+
throw new RangeError("Principal mutation timeout must be a positive integer no greater than 2147483647 milliseconds");
|
|
7
|
+
return async (options, submit) => {
|
|
8
|
+
const controller = new AbortController();
|
|
9
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
10
|
+
try {
|
|
11
|
+
return await submit({
|
|
12
|
+
...options,
|
|
13
|
+
signal: options?.signal
|
|
14
|
+
? AbortSignal.any([options.signal, controller.signal])
|
|
15
|
+
: controller.signal,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=principalMutationDeadline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principalMutationDeadline.js","sourceRoot":"","sources":["principalMutationDeadline.ts"],"names":[],"mappings":"AAEA,oFAAoF;AACpF,MAAM,UAAU,iCAAiC,CAAC,SAAS,GAAG,MAAM;IAClE,IACE,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC;QAChC,SAAS,IAAI,CAAC;QACd,SAAS,GAAG,aAAa;QAEzB,MAAM,IAAI,UAAU,CAClB,+FAA+F,CAChG,CAAC;IACJ,OAAO,KAAK,EACV,OAAyC,EACzC,MAAqD,EACzC,EAAE;QACd,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;QAC9D,IAAI,CAAC;YACH,OAAO,MAAM,MAAM,CAAC;gBAClB,GAAG,OAAO;gBACV,MAAM,EAAE,OAAO,EAAE,MAAM;oBACrB,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;oBACtD,CAAC,CAAC,UAAU,CAAC,MAAM;aACtB,CAAC,CAAC;QACL,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type { RequestResultOptions } from \"@tearleads/api-client\";\n\n/** One host-configurable deadline covers dispatch and all preparation exchanges. */\nexport function createPrincipalMutationDispatcher(timeoutMs = 60_000) {\n if (\n !Number.isSafeInteger(timeoutMs) ||\n timeoutMs <= 0 ||\n timeoutMs > 2_147_483_647\n )\n throw new RangeError(\n \"Principal mutation timeout must be a positive integer no greater than 2147483647 milliseconds\",\n );\n return async <T>(\n options: RequestResultOptions | undefined,\n submit: (options: RequestResultOptions) => Promise<T>,\n ): Promise<T> => {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await submit({\n ...options,\n signal: options?.signal\n ? AbortSignal.any([options.signal, controller.signal])\n : controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n };\n}\n"]}
|
|
@@ -12,6 +12,7 @@ import type { Events } from "./events.js";
|
|
|
12
12
|
import type { Identity } from "./identity.js";
|
|
13
13
|
import type { Network } from "./network.js";
|
|
14
14
|
import { type PrincipalHistoryKeyProvider } from "./principalHistoryProtection.js";
|
|
15
|
+
import { type PrincipalMutationApi } from "./principalMutationApi.js";
|
|
15
16
|
import type { Session } from "./session/sessionTypes.js";
|
|
16
17
|
import type { SyncBillingGate } from "./syncBillingGate.js";
|
|
17
18
|
type HostWorkflowRuntimeUtilInput = Omit<WorkflowRuntimeUtilInput, "reportSecurityIncident">;
|
|
@@ -25,7 +26,7 @@ export interface Runtime {
|
|
|
25
26
|
subscribe(listener: RuntimeListener): () => void;
|
|
26
27
|
}
|
|
27
28
|
export interface InternalWorkflowRuntimeInput extends WorkflowRuntimeGroups {
|
|
28
|
-
readonly apiClient:
|
|
29
|
+
readonly apiClient: PrincipalMutationApi;
|
|
29
30
|
readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;
|
|
30
31
|
readonly withPrincipalHistoryProtection?: PrincipalHistoryProtectionLease | undefined;
|
|
31
32
|
}
|
|
@@ -52,6 +53,7 @@ interface WorkflowRuntimeDependencies {
|
|
|
52
53
|
network: Network;
|
|
53
54
|
peerScope?: string | null;
|
|
54
55
|
principalHistoryKeyProvider?: PrincipalHistoryKeyProvider | undefined;
|
|
56
|
+
principalMutationTimeoutMs?: number | undefined;
|
|
55
57
|
reportSecurityIncident: SecurityIncidentReporter;
|
|
56
58
|
session: Session;
|
|
57
59
|
syncBillingGate?: SyncBillingGate | undefined;
|
|
@@ -3,8 +3,36 @@ import { unavailableExecSql } from "../data/sqlite/sqlSchema.js";
|
|
|
3
3
|
import { createApiUserIdentitySource, createTrustedUserIdentityService, } from "../data/trustedUserIdentity/index.js";
|
|
4
4
|
import { createListenerSet } from "./listenerSet.js";
|
|
5
5
|
import { createPrincipalHistoryProtectionCustody, } from "./principalHistoryProtection.js";
|
|
6
|
+
import { createPrincipalMutationApiCustody, } from "./principalMutationApi.js";
|
|
6
7
|
import { adoptSessionRootContainer } from "./rootContainerAdoption.js";
|
|
7
8
|
import { acknowledgedSessionRoot } from "./session/sessionRootAuthority.js";
|
|
9
|
+
function createRuntimeMutationApi(dependencies, readGeneration) {
|
|
10
|
+
return createPrincipalMutationApiCustody({
|
|
11
|
+
api: dependencies.api,
|
|
12
|
+
principalMutationTimeoutMs: dependencies.principalMutationTimeoutMs,
|
|
13
|
+
readScope: () => {
|
|
14
|
+
const { signingKeyPair, signingFingerprint } = dependencies.identity;
|
|
15
|
+
const { userId, isAuthenticated } = dependencies.session;
|
|
16
|
+
const identityTrustDomain = dependencies.identityTrustDomain;
|
|
17
|
+
if (!isAuthenticated ||
|
|
18
|
+
!userId ||
|
|
19
|
+
!signingKeyPair ||
|
|
20
|
+
!signingFingerprint ||
|
|
21
|
+
!identityTrustDomain ||
|
|
22
|
+
dependencies.database.status !== "ready")
|
|
23
|
+
return null;
|
|
24
|
+
return {
|
|
25
|
+
database: dependencies.getDomainScope(),
|
|
26
|
+
execSql: dependencies.database.requireExecSql("principal mutation"),
|
|
27
|
+
generation: readGeneration(),
|
|
28
|
+
identityTrustDomain,
|
|
29
|
+
signingFingerprint,
|
|
30
|
+
signingKeyPair,
|
|
31
|
+
userId,
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
8
36
|
export function createRuntime(dependencies) {
|
|
9
37
|
const trustedUserIdentityService = createTrustedUserIdentityService({
|
|
10
38
|
getExecSql: () => dependencies.database.status === "ready"
|
|
@@ -50,7 +78,7 @@ export function createRuntime(dependencies) {
|
|
|
50
78
|
};
|
|
51
79
|
},
|
|
52
80
|
});
|
|
53
|
-
const runtimeInput = createRuntimeInputFactory(dependencies, resolveTrustedUserIdentity, historyProtection.bind);
|
|
81
|
+
const runtimeInput = createRuntimeInputFactory(dependencies, resolveTrustedUserIdentity, historyProtection.bind, createRuntimeMutationApi(dependencies, () => runtimeSubscription.mutationGeneration).bind);
|
|
54
82
|
return {
|
|
55
83
|
retirePrincipalHistoryProtection: historyProtection.retire,
|
|
56
84
|
get sessionGeneration() {
|
|
@@ -81,6 +109,7 @@ function createRuntimeSubscription(dependencies) {
|
|
|
81
109
|
let version = 0;
|
|
82
110
|
let sessionGeneration = 0;
|
|
83
111
|
let recoveryGeneration = 0;
|
|
112
|
+
let mutationGeneration = 0;
|
|
84
113
|
let sessionSnapshot = dependencies.session.snapshot;
|
|
85
114
|
const notifyListeners = () => {
|
|
86
115
|
version += 1;
|
|
@@ -88,6 +117,7 @@ function createRuntimeSubscription(dependencies) {
|
|
|
88
117
|
};
|
|
89
118
|
const notifyAuthority = () => {
|
|
90
119
|
recoveryGeneration += 1;
|
|
120
|
+
mutationGeneration += 1;
|
|
91
121
|
notifyListeners();
|
|
92
122
|
};
|
|
93
123
|
dependencies.database.subscribe(notifyAuthority);
|
|
@@ -100,11 +130,16 @@ function createRuntimeSubscription(dependencies) {
|
|
|
100
130
|
sessionGeneration += 1;
|
|
101
131
|
recoveryGeneration += 1;
|
|
102
132
|
}
|
|
133
|
+
if (sessionScopeChanged(sessionSnapshot, next))
|
|
134
|
+
mutationGeneration += 1;
|
|
103
135
|
sessionSnapshot = next;
|
|
104
136
|
notifyListeners();
|
|
105
137
|
});
|
|
106
138
|
dependencies.syncBillingGate?.subscribe(notifyListeners);
|
|
107
139
|
return {
|
|
140
|
+
get mutationGeneration() {
|
|
141
|
+
return mutationGeneration;
|
|
142
|
+
},
|
|
108
143
|
get recoveryGeneration() {
|
|
109
144
|
return recoveryGeneration;
|
|
110
145
|
},
|
|
@@ -132,13 +167,17 @@ function sessionAuthInput({ session, identity, }) {
|
|
|
132
167
|
}
|
|
133
168
|
/** Fields whose change invalidates work keyed on the session's authority. */
|
|
134
169
|
function sessionAuthorityChanged(previous, next) {
|
|
135
|
-
return (next.authToken !== previous.authToken ||
|
|
136
|
-
|
|
170
|
+
return (next.authToken !== previous.authToken || sessionScopeChanged(previous, next));
|
|
171
|
+
}
|
|
172
|
+
// A transport token renewal cannot turn a definite write refusal into saved work.
|
|
173
|
+
// Journal custody still expires on every change to its identity or storage scope.
|
|
174
|
+
function sessionScopeChanged(previous, next) {
|
|
175
|
+
return (next.isAuthenticated !== previous.isAuthenticated ||
|
|
137
176
|
next.isRoot !== previous.isRoot ||
|
|
138
177
|
next.organizationId !== previous.organizationId ||
|
|
139
178
|
next.userId !== previous.userId);
|
|
140
179
|
}
|
|
141
|
-
function createRuntimeInputFactory(dependencies, resolveTrustedUserIdentity, bindHistoryProtection) {
|
|
180
|
+
function createRuntimeInputFactory(dependencies, resolveTrustedUserIdentity, bindHistoryProtection, bindMutationApi) {
|
|
142
181
|
let auth;
|
|
143
182
|
let crypto;
|
|
144
183
|
let infra;
|
|
@@ -190,7 +229,7 @@ function createRuntimeInputFactory(dependencies, resolveTrustedUserIdentity, bin
|
|
|
190
229
|
};
|
|
191
230
|
}
|
|
192
231
|
return {
|
|
193
|
-
apiClient:
|
|
232
|
+
apiClient: bindMutationApi(),
|
|
194
233
|
auth,
|
|
195
234
|
crypto,
|
|
196
235
|
infra,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflowRuntime.js","sourceRoot":"","sources":["workflowRuntime.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport type { DocumentProjectorRegistry } from \"../data/documents/documentKinds\";\nimport type { DomainScope } from \"../data/domainScope\";\nimport { runWithSecurityIncidentReporting } from \"../data/keyingProjectionVerification/error\";\nimport type { PrincipalHistoryProtectionLease } from \"../data/principals/principalHistoryProtection\";\nimport type { SecurityIncidentReporter } from \"../data/securityIncidents\";\nimport { unavailableExecSql } from \"../data/sqlite/sqlSchema\";\nimport {\n createApiUserIdentitySource,\n createTrustedUserIdentityService,\n type LocalUserIdentityCandidate,\n type TrustedUserIdentityResolver,\n} from \"../data/trustedUserIdentity\";\nimport type { ContainerContentsRootAdopter } from \"../workflows/container-contents/runtime\";\nimport type {\n WorkflowRuntimeAuthInput,\n WorkflowRuntimeCryptoInput,\n WorkflowRuntimeGroups,\n WorkflowRuntimeInfraInput,\n WorkflowRuntimeStateInput,\n WorkflowRuntimeUtilInput,\n} from \"../workflows/runtimeInput\";\nimport type { Blobs } from \"./blobs\";\nimport type { Database } from \"./database\";\nimport type { Events } from \"./events\";\nimport type { Identity } from \"./identity\";\nimport { createListenerSet } from \"./listenerSet\";\nimport type { Network } from \"./network\";\nimport {\n createPrincipalHistoryProtectionCustody,\n type PrincipalHistoryKeyProvider,\n} from \"./principalHistoryProtection\";\nimport { adoptSessionRootContainer } from \"./rootContainerAdoption\";\nimport { acknowledgedSessionRoot } from \"./session/sessionRootAuthority\";\nimport type { Session, SessionSnapshot } from \"./session/sessionTypes\";\nimport type { SyncBillingGate } from \"./syncBillingGate\";\n\ntype HostWorkflowRuntimeUtilInput = Omit<\n WorkflowRuntimeUtilInput,\n \"reportSecurityIncident\"\n>;\n\nexport interface WorkflowRuntimeInput\n extends Omit<WorkflowRuntimeGroups, \"util\"> {\n readonly util: HostWorkflowRuntimeUtilInput;\n}\n\nexport type RuntimeListener = () => void;\n\nexport interface Runtime {\n readonly version: number;\n input(containerId?: string | null | undefined): WorkflowRuntimeInput;\n subscribe(listener: RuntimeListener): () => void;\n}\n\nexport interface InternalWorkflowRuntimeInput extends WorkflowRuntimeGroups {\n readonly apiClient: ApiClient;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly withPrincipalHistoryProtection?:\n | PrincipalHistoryProtectionLease\n | undefined;\n}\n\nexport interface InternalRuntime {\n retirePrincipalHistoryProtection(): void;\n readonly sessionGeneration: number;\n readonly adoptRootContainer: ContainerContentsRootAdopter;\n readonly publicRuntime: Runtime;\n boundUserId(signingKeyFingerprint: string): Promise<string | null>;\n pinLocalUserIdentity(\n userId: string,\n candidate: LocalUserIdentityCandidate,\n ): Promise<void>;\n workflowInput(\n containerId?: string | null | undefined,\n ): InternalWorkflowRuntimeInput;\n}\n\ninterface WorkflowRuntimeDependencies {\n api: ApiClient;\n blobs: Blobs;\n database: Database;\n documentProjectors: DocumentProjectorRegistry;\n events: Events;\n getDomainScope: () => DomainScope;\n identity: Identity;\n identityTrustDomain: string | null;\n log: (message: string) => void;\n logError: (message: string | Error, cause?: unknown) => void;\n network: Network;\n peerScope?: string | null;\n principalHistoryKeyProvider?: PrincipalHistoryKeyProvider | undefined;\n reportSecurityIncident: SecurityIncidentReporter;\n session: Session;\n syncBillingGate?: SyncBillingGate | undefined;\n}\n\nexport function createRuntime(\n dependencies: WorkflowRuntimeDependencies,\n): InternalRuntime {\n const trustedUserIdentityService = createTrustedUserIdentityService({\n getExecSql: () =>\n dependencies.database.status === \"ready\"\n ? dependencies.database.requireExecSql(\"trusted user identity\")\n : null,\n getLocalIdentity: () => {\n const encapsulationKeyPair = dependencies.identity.encapsulationKeyPair;\n const signingKeyPair = dependencies.identity.signingKeyPair;\n if (!encapsulationKeyPair || !signingKeyPair) {\n return null;\n }\n return {\n encapsulationPublicKey: encapsulationKeyPair.publicKey,\n signingKeyFingerprint: dependencies.identity.signingFingerprint,\n signingPublicKey: signingKeyPair.signingPublicKey,\n };\n },\n getLocalUserId: () => dependencies.session.userId,\n identityTrustDomain: dependencies.identityTrustDomain,\n remoteSource: createApiUserIdentitySource(dependencies.api),\n });\n const resolveTrustedUserIdentity: TrustedUserIdentityResolver = (userId) =>\n runWithSecurityIncidentReporting(\n dependencies.reportSecurityIncident,\n {\n objectId: userId,\n objectKind: \"user\",\n operation: \"user.identity.resolve\",\n organizationId: dependencies.session.organizationId,\n },\n () => trustedUserIdentityService.resolve(userId),\n );\n const runtimeSubscription = createRuntimeSubscription(dependencies);\n const historyProtection = createPrincipalHistoryProtectionCustody({\n keyProvider: dependencies.principalHistoryKeyProvider,\n readScope: () => {\n const signingFingerprint = dependencies.identity.signingFingerprint;\n const identityTrustDomain = dependencies.identityTrustDomain;\n if (\n !signingFingerprint ||\n !identityTrustDomain ||\n dependencies.database.status !== \"ready\"\n )\n return null;\n return {\n signingFingerprint,\n identityTrustDomain,\n database: dependencies.getDomainScope(),\n generation: runtimeSubscription.recoveryGeneration,\n };\n },\n });\n const runtimeInput = createRuntimeInputFactory(\n dependencies,\n resolveTrustedUserIdentity,\n historyProtection.bind,\n );\n\n return {\n retirePrincipalHistoryProtection: historyProtection.retire,\n get sessionGeneration() {\n return runtimeSubscription.sessionGeneration;\n },\n adoptRootContainer: (input) =>\n adoptSessionRootContainer(dependencies, input),\n boundUserId: (signingKeyFingerprint) =>\n trustedUserIdentityService.boundUserId(signingKeyFingerprint),\n async pinLocalUserIdentity(userId, candidate) {\n await runWithSecurityIncidentReporting(\n dependencies.reportSecurityIncident,\n {\n objectId: userId,\n objectKind: \"user\",\n operation: \"user.identity.pin_local\",\n organizationId: dependencies.session.organizationId,\n },\n () => trustedUserIdentityService.pinLocal(userId, candidate),\n );\n },\n publicRuntime: {\n get version() {\n return runtimeSubscription.version;\n },\n input: runtimeInput.hostInput,\n subscribe: runtimeSubscription.subscribe,\n },\n workflowInput: runtimeInput.workflowInput,\n };\n}\n\nfunction createRuntimeSubscription(dependencies: WorkflowRuntimeDependencies) {\n const listeners = createListenerSet();\n let version = 0;\n let sessionGeneration = 0;\n let recoveryGeneration = 0;\n let sessionSnapshot = dependencies.session.snapshot;\n const notifyListeners = () => {\n version += 1;\n listeners.notify();\n };\n\n const notifyAuthority = () => {\n recoveryGeneration += 1;\n notifyListeners();\n };\n dependencies.database.subscribe(notifyAuthority);\n dependencies.events.subscribe(notifyListeners);\n dependencies.identity.subscribe(notifyAuthority);\n dependencies.network.subscribe(() => notifyListeners());\n dependencies.session.subscribe(() => {\n const next = dependencies.session.snapshot;\n if (sessionAuthorityChanged(sessionSnapshot, next)) {\n sessionGeneration += 1;\n recoveryGeneration += 1;\n }\n sessionSnapshot = next;\n notifyListeners();\n });\n dependencies.syncBillingGate?.subscribe(notifyListeners);\n\n return {\n get recoveryGeneration() {\n return recoveryGeneration;\n },\n get sessionGeneration() {\n return sessionGeneration;\n },\n get version() {\n return version;\n },\n subscribe(listener: RuntimeListener): () => void {\n return listeners.subscribe(listener);\n },\n };\n}\n\ninterface RuntimeInputFactory {\n hostInput(containerId?: string | null | undefined): WorkflowRuntimeInput;\n workflowInput(\n containerId?: string | null | undefined,\n ): InternalWorkflowRuntimeInput;\n}\n\nfunction sessionAuthInput({\n session,\n identity,\n}: WorkflowRuntimeDependencies): WorkflowRuntimeAuthInput {\n const signingFingerprint = identity.snapshot.signingFingerprint;\n return {\n defaultOrganizationId: session.defaultOrganizationId,\n rootContainerId: acknowledgedSessionRoot(session, signingFingerprint),\n isAuthenticated: session.isAuthenticated,\n isRoot: session.isRoot,\n organizationId: session.organizationId,\n userId: session.userId,\n };\n}\n\n/** Fields whose change invalidates work keyed on the session's authority. */\nfunction sessionAuthorityChanged(\n previous: SessionSnapshot,\n next: SessionSnapshot,\n): boolean {\n return (\n next.authToken !== previous.authToken ||\n next.isAuthenticated !== previous.isAuthenticated ||\n next.isRoot !== previous.isRoot ||\n next.organizationId !== previous.organizationId ||\n next.userId !== previous.userId\n );\n}\n\nfunction createRuntimeInputFactory(\n dependencies: WorkflowRuntimeDependencies,\n resolveTrustedUserIdentity: TrustedUserIdentityResolver,\n bindHistoryProtection: () => PrincipalHistoryProtectionLease | undefined,\n): RuntimeInputFactory {\n let auth: WorkflowRuntimeAuthInput | undefined;\n let crypto: WorkflowRuntimeCryptoInput | undefined;\n let infra: WorkflowRuntimeInfraInput | undefined;\n let state: WorkflowRuntimeStateInput | undefined;\n let util: WorkflowRuntimeUtilInput | undefined;\n let hostUtil: HostWorkflowRuntimeUtilInput | undefined;\n\n const workflowInput = (\n containerId?: string | null | undefined,\n ): InternalWorkflowRuntimeInput => {\n const dbStatus = dependencies.database.status;\n const execSql =\n dbStatus === \"ready\"\n ? dependencies.database.requireExecSql(\"tearleads.runtime.input\")\n : unavailableExecSql;\n const nextContainerId =\n (containerId === undefined\n ? dependencies.session.containerId\n : containerId) ?? null;\n\n auth = reuseIfShallowEqual(auth, sessionAuthInput(dependencies));\n crypto = reuseIfShallowEqual(crypto, {\n encapsulationKeyPair: dependencies.identity.encapsulationKeyPair,\n signingFingerprint: dependencies.identity.signingFingerprint,\n signingKeyPair: dependencies.identity.signingKeyPair,\n });\n infra = reuseIfShallowEqual(infra, {\n blobStore: dependencies.blobs.store,\n dbStatus,\n documentProjectors: dependencies.documentProjectors,\n // Runtime consumers branch on dbStatus before touching SQLite. The\n // fallback preserves the runtime shape and catches lifecycle bugs.\n execSql,\n });\n state = reuseIfShallowEqual(state, {\n containerId: nextContainerId,\n domainScope: dependencies.getDomainScope(),\n events: dependencies.events.events,\n // Resolved sync-online: workflows treat local-only mode exactly like being\n // offline (no server I/O), while `tearleads.network` stays the truthful\n // connectivity signal for host/UI. See Session.syncEnabled.\n online: dependencies.network.online && dependencies.session.syncEnabled,\n peerScope: dependencies.peerScope ?? null,\n serverEventsConnectionGeneration:\n dependencies.events.connectionGeneration,\n });\n if (\n !util ||\n util.log !== dependencies.log ||\n util.logError !== dependencies.logError ||\n util.reportSecurityIncident !== dependencies.reportSecurityIncident\n ) {\n util = {\n isRemoteSyncBlocked: (organizationId) =>\n dependencies.syncBillingGate?.isBlockedForOrganization(\n organizationId,\n ) ?? false,\n log: dependencies.log,\n logError: dependencies.logError,\n reportSecurityIncident: dependencies.reportSecurityIncident,\n };\n }\n\n return {\n apiClient: dependencies.api,\n auth,\n crypto,\n infra,\n state,\n util,\n resolveTrustedUserIdentity,\n withPrincipalHistoryProtection: bindHistoryProtection(),\n };\n };\n\n return {\n hostInput(containerId) {\n const {\n apiClient: _apiClient,\n resolveTrustedUserIdentity: _resolveTrustedUserIdentity,\n withPrincipalHistoryProtection: _withPrincipalHistoryProtection,\n util: workflowUtil,\n ...input\n } = workflowInput(containerId);\n hostUtil = reuseIfShallowEqual(hostUtil, {\n isRemoteSyncBlocked: workflowUtil.isRemoteSyncBlocked,\n log: workflowUtil.log,\n logError: workflowUtil.logError,\n });\n return { ...input, util: hostUtil };\n },\n workflowInput,\n };\n}\n\n/**\n * Reuses the current runtime-input group when every field of the freshly\n * constructed `next` literal is reference-equal, so unchanged groups keep a\n * stable identity across runtime reads.\n */\nfunction reuseIfShallowEqual<T extends object>(\n current: T | undefined,\n next: T,\n): T {\n if (!current) {\n return next;\n }\n for (const key of Object.keys(next)) {\n if (Reflect.get(current, key) !== Reflect.get(next, key)) {\n return next;\n }\n }\n return current;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"workflowRuntime.js","sourceRoot":"","sources":["workflowRuntime.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport type { DocumentProjectorRegistry } from \"../data/documents/documentKinds\";\nimport type { DomainScope } from \"../data/domainScope\";\nimport { runWithSecurityIncidentReporting } from \"../data/keyingProjectionVerification/error\";\nimport type { PrincipalHistoryProtectionLease } from \"../data/principals/principalHistoryProtection\";\nimport type { SecurityIncidentReporter } from \"../data/securityIncidents\";\nimport { unavailableExecSql } from \"../data/sqlite/sqlSchema\";\nimport {\n createApiUserIdentitySource,\n createTrustedUserIdentityService,\n type LocalUserIdentityCandidate,\n type TrustedUserIdentityResolver,\n} from \"../data/trustedUserIdentity\";\nimport type { ContainerContentsRootAdopter } from \"../workflows/container-contents/runtime\";\nimport type {\n WorkflowRuntimeAuthInput,\n WorkflowRuntimeCryptoInput,\n WorkflowRuntimeGroups,\n WorkflowRuntimeInfraInput,\n WorkflowRuntimeStateInput,\n WorkflowRuntimeUtilInput,\n} from \"../workflows/runtimeInput\";\nimport type { Blobs } from \"./blobs\";\nimport type { Database } from \"./database\";\nimport type { Events } from \"./events\";\nimport type { Identity } from \"./identity\";\nimport { createListenerSet } from \"./listenerSet\";\nimport type { Network } from \"./network\";\nimport {\n createPrincipalHistoryProtectionCustody,\n type PrincipalHistoryKeyProvider,\n} from \"./principalHistoryProtection\";\nimport {\n createPrincipalMutationApiCustody,\n type PrincipalMutationApi,\n} from \"./principalMutationApi\";\nimport { adoptSessionRootContainer } from \"./rootContainerAdoption\";\nimport { acknowledgedSessionRoot } from \"./session/sessionRootAuthority\";\nimport type { Session, SessionSnapshot } from \"./session/sessionTypes\";\nimport type { SyncBillingGate } from \"./syncBillingGate\";\n\ntype HostWorkflowRuntimeUtilInput = Omit<\n WorkflowRuntimeUtilInput,\n \"reportSecurityIncident\"\n>;\n\nexport interface WorkflowRuntimeInput\n extends Omit<WorkflowRuntimeGroups, \"util\"> {\n readonly util: HostWorkflowRuntimeUtilInput;\n}\n\nexport type RuntimeListener = () => void;\n\nexport interface Runtime {\n readonly version: number;\n input(containerId?: string | null | undefined): WorkflowRuntimeInput;\n subscribe(listener: RuntimeListener): () => void;\n}\n\nexport interface InternalWorkflowRuntimeInput extends WorkflowRuntimeGroups {\n readonly apiClient: PrincipalMutationApi;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly withPrincipalHistoryProtection?:\n | PrincipalHistoryProtectionLease\n | undefined;\n}\n\nexport interface InternalRuntime {\n retirePrincipalHistoryProtection(): void;\n readonly sessionGeneration: number;\n readonly adoptRootContainer: ContainerContentsRootAdopter;\n readonly publicRuntime: Runtime;\n boundUserId(signingKeyFingerprint: string): Promise<string | null>;\n pinLocalUserIdentity(\n userId: string,\n candidate: LocalUserIdentityCandidate,\n ): Promise<void>;\n workflowInput(\n containerId?: string | null | undefined,\n ): InternalWorkflowRuntimeInput;\n}\n\ninterface WorkflowRuntimeDependencies {\n api: ApiClient;\n blobs: Blobs;\n database: Database;\n documentProjectors: DocumentProjectorRegistry;\n events: Events;\n getDomainScope: () => DomainScope;\n identity: Identity;\n identityTrustDomain: string | null;\n log: (message: string) => void;\n logError: (message: string | Error, cause?: unknown) => void;\n network: Network;\n peerScope?: string | null;\n principalHistoryKeyProvider?: PrincipalHistoryKeyProvider | undefined;\n principalMutationTimeoutMs?: number | undefined;\n reportSecurityIncident: SecurityIncidentReporter;\n session: Session;\n syncBillingGate?: SyncBillingGate | undefined;\n}\n\nfunction createRuntimeMutationApi(\n dependencies: WorkflowRuntimeDependencies,\n readGeneration: () => number,\n) {\n return createPrincipalMutationApiCustody({\n api: dependencies.api,\n principalMutationTimeoutMs: dependencies.principalMutationTimeoutMs,\n readScope: () => {\n const { signingKeyPair, signingFingerprint } = dependencies.identity;\n const { userId, isAuthenticated } = dependencies.session;\n const identityTrustDomain = dependencies.identityTrustDomain;\n if (\n !isAuthenticated ||\n !userId ||\n !signingKeyPair ||\n !signingFingerprint ||\n !identityTrustDomain ||\n dependencies.database.status !== \"ready\"\n )\n return null;\n return {\n database: dependencies.getDomainScope(),\n execSql: dependencies.database.requireExecSql(\"principal mutation\"),\n generation: readGeneration(),\n identityTrustDomain,\n signingFingerprint,\n signingKeyPair,\n userId,\n };\n },\n });\n}\n\nexport function createRuntime(\n dependencies: WorkflowRuntimeDependencies,\n): InternalRuntime {\n const trustedUserIdentityService = createTrustedUserIdentityService({\n getExecSql: () =>\n dependencies.database.status === \"ready\"\n ? dependencies.database.requireExecSql(\"trusted user identity\")\n : null,\n getLocalIdentity: () => {\n const encapsulationKeyPair = dependencies.identity.encapsulationKeyPair;\n const signingKeyPair = dependencies.identity.signingKeyPair;\n if (!encapsulationKeyPair || !signingKeyPair) {\n return null;\n }\n return {\n encapsulationPublicKey: encapsulationKeyPair.publicKey,\n signingKeyFingerprint: dependencies.identity.signingFingerprint,\n signingPublicKey: signingKeyPair.signingPublicKey,\n };\n },\n getLocalUserId: () => dependencies.session.userId,\n identityTrustDomain: dependencies.identityTrustDomain,\n remoteSource: createApiUserIdentitySource(dependencies.api),\n });\n const resolveTrustedUserIdentity: TrustedUserIdentityResolver = (userId) =>\n runWithSecurityIncidentReporting(\n dependencies.reportSecurityIncident,\n {\n objectId: userId,\n objectKind: \"user\",\n operation: \"user.identity.resolve\",\n organizationId: dependencies.session.organizationId,\n },\n () => trustedUserIdentityService.resolve(userId),\n );\n const runtimeSubscription = createRuntimeSubscription(dependencies);\n const historyProtection = createPrincipalHistoryProtectionCustody({\n keyProvider: dependencies.principalHistoryKeyProvider,\n readScope: () => {\n const signingFingerprint = dependencies.identity.signingFingerprint;\n const identityTrustDomain = dependencies.identityTrustDomain;\n if (\n !signingFingerprint ||\n !identityTrustDomain ||\n dependencies.database.status !== \"ready\"\n )\n return null;\n return {\n signingFingerprint,\n identityTrustDomain,\n database: dependencies.getDomainScope(),\n generation: runtimeSubscription.recoveryGeneration,\n };\n },\n });\n const runtimeInput = createRuntimeInputFactory(\n dependencies,\n resolveTrustedUserIdentity,\n historyProtection.bind,\n createRuntimeMutationApi(\n dependencies,\n () => runtimeSubscription.mutationGeneration,\n ).bind,\n );\n\n return {\n retirePrincipalHistoryProtection: historyProtection.retire,\n get sessionGeneration() {\n return runtimeSubscription.sessionGeneration;\n },\n adoptRootContainer: (input) =>\n adoptSessionRootContainer(dependencies, input),\n boundUserId: (signingKeyFingerprint) =>\n trustedUserIdentityService.boundUserId(signingKeyFingerprint),\n async pinLocalUserIdentity(userId, candidate) {\n await runWithSecurityIncidentReporting(\n dependencies.reportSecurityIncident,\n {\n objectId: userId,\n objectKind: \"user\",\n operation: \"user.identity.pin_local\",\n organizationId: dependencies.session.organizationId,\n },\n () => trustedUserIdentityService.pinLocal(userId, candidate),\n );\n },\n publicRuntime: {\n get version() {\n return runtimeSubscription.version;\n },\n input: runtimeInput.hostInput,\n subscribe: runtimeSubscription.subscribe,\n },\n workflowInput: runtimeInput.workflowInput,\n };\n}\n\nfunction createRuntimeSubscription(dependencies: WorkflowRuntimeDependencies) {\n const listeners = createListenerSet();\n let version = 0;\n let sessionGeneration = 0;\n let recoveryGeneration = 0;\n let mutationGeneration = 0;\n let sessionSnapshot = dependencies.session.snapshot;\n const notifyListeners = () => {\n version += 1;\n listeners.notify();\n };\n\n const notifyAuthority = () => {\n recoveryGeneration += 1;\n mutationGeneration += 1;\n notifyListeners();\n };\n dependencies.database.subscribe(notifyAuthority);\n dependencies.events.subscribe(notifyListeners);\n dependencies.identity.subscribe(notifyAuthority);\n dependencies.network.subscribe(() => notifyListeners());\n dependencies.session.subscribe(() => {\n const next = dependencies.session.snapshot;\n if (sessionAuthorityChanged(sessionSnapshot, next)) {\n sessionGeneration += 1;\n recoveryGeneration += 1;\n }\n if (sessionScopeChanged(sessionSnapshot, next)) mutationGeneration += 1;\n sessionSnapshot = next;\n notifyListeners();\n });\n dependencies.syncBillingGate?.subscribe(notifyListeners);\n\n return {\n get mutationGeneration() {\n return mutationGeneration;\n },\n get recoveryGeneration() {\n return recoveryGeneration;\n },\n get sessionGeneration() {\n return sessionGeneration;\n },\n get version() {\n return version;\n },\n subscribe(listener: RuntimeListener): () => void {\n return listeners.subscribe(listener);\n },\n };\n}\n\ninterface RuntimeInputFactory {\n hostInput(containerId?: string | null | undefined): WorkflowRuntimeInput;\n workflowInput(\n containerId?: string | null | undefined,\n ): InternalWorkflowRuntimeInput;\n}\n\nfunction sessionAuthInput({\n session,\n identity,\n}: WorkflowRuntimeDependencies): WorkflowRuntimeAuthInput {\n const signingFingerprint = identity.snapshot.signingFingerprint;\n return {\n defaultOrganizationId: session.defaultOrganizationId,\n rootContainerId: acknowledgedSessionRoot(session, signingFingerprint),\n isAuthenticated: session.isAuthenticated,\n isRoot: session.isRoot,\n organizationId: session.organizationId,\n userId: session.userId,\n };\n}\n\n/** Fields whose change invalidates work keyed on the session's authority. */\nfunction sessionAuthorityChanged(\n previous: SessionSnapshot,\n next: SessionSnapshot,\n): boolean {\n return (\n next.authToken !== previous.authToken || sessionScopeChanged(previous, next)\n );\n}\n\n// A transport token renewal cannot turn a definite write refusal into saved work.\n// Journal custody still expires on every change to its identity or storage scope.\nfunction sessionScopeChanged(\n previous: SessionSnapshot,\n next: SessionSnapshot,\n): boolean {\n return (\n next.isAuthenticated !== previous.isAuthenticated ||\n next.isRoot !== previous.isRoot ||\n next.organizationId !== previous.organizationId ||\n next.userId !== previous.userId\n );\n}\n\nfunction createRuntimeInputFactory(\n dependencies: WorkflowRuntimeDependencies,\n resolveTrustedUserIdentity: TrustedUserIdentityResolver,\n bindHistoryProtection: () => PrincipalHistoryProtectionLease | undefined,\n bindMutationApi: () => PrincipalMutationApi,\n): RuntimeInputFactory {\n let auth: WorkflowRuntimeAuthInput | undefined;\n let crypto: WorkflowRuntimeCryptoInput | undefined;\n let infra: WorkflowRuntimeInfraInput | undefined;\n let state: WorkflowRuntimeStateInput | undefined;\n let util: WorkflowRuntimeUtilInput | undefined;\n let hostUtil: HostWorkflowRuntimeUtilInput | undefined;\n\n const workflowInput = (\n containerId?: string | null | undefined,\n ): InternalWorkflowRuntimeInput => {\n const dbStatus = dependencies.database.status;\n const execSql =\n dbStatus === \"ready\"\n ? dependencies.database.requireExecSql(\"tearleads.runtime.input\")\n : unavailableExecSql;\n const nextContainerId =\n (containerId === undefined\n ? dependencies.session.containerId\n : containerId) ?? null;\n\n auth = reuseIfShallowEqual(auth, sessionAuthInput(dependencies));\n crypto = reuseIfShallowEqual(crypto, {\n encapsulationKeyPair: dependencies.identity.encapsulationKeyPair,\n signingFingerprint: dependencies.identity.signingFingerprint,\n signingKeyPair: dependencies.identity.signingKeyPair,\n });\n infra = reuseIfShallowEqual(infra, {\n blobStore: dependencies.blobs.store,\n dbStatus,\n documentProjectors: dependencies.documentProjectors,\n // Runtime consumers branch on dbStatus before touching SQLite. The\n // fallback preserves the runtime shape and catches lifecycle bugs.\n execSql,\n });\n state = reuseIfShallowEqual(state, {\n containerId: nextContainerId,\n domainScope: dependencies.getDomainScope(),\n events: dependencies.events.events,\n // Resolved sync-online: workflows treat local-only mode exactly like being\n // offline (no server I/O), while `tearleads.network` stays the truthful\n // connectivity signal for host/UI. See Session.syncEnabled.\n online: dependencies.network.online && dependencies.session.syncEnabled,\n peerScope: dependencies.peerScope ?? null,\n serverEventsConnectionGeneration:\n dependencies.events.connectionGeneration,\n });\n if (\n !util ||\n util.log !== dependencies.log ||\n util.logError !== dependencies.logError ||\n util.reportSecurityIncident !== dependencies.reportSecurityIncident\n ) {\n util = {\n isRemoteSyncBlocked: (organizationId) =>\n dependencies.syncBillingGate?.isBlockedForOrganization(\n organizationId,\n ) ?? false,\n log: dependencies.log,\n logError: dependencies.logError,\n reportSecurityIncident: dependencies.reportSecurityIncident,\n };\n }\n\n return {\n apiClient: bindMutationApi(),\n auth,\n crypto,\n infra,\n state,\n util,\n resolveTrustedUserIdentity,\n withPrincipalHistoryProtection: bindHistoryProtection(),\n };\n };\n\n return {\n hostInput(containerId) {\n const {\n apiClient: _apiClient,\n resolveTrustedUserIdentity: _resolveTrustedUserIdentity,\n withPrincipalHistoryProtection: _withPrincipalHistoryProtection,\n util: workflowUtil,\n ...input\n } = workflowInput(containerId);\n hostUtil = reuseIfShallowEqual(hostUtil, {\n isRemoteSyncBlocked: workflowUtil.isRemoteSyncBlocked,\n log: workflowUtil.log,\n logError: workflowUtil.logError,\n });\n return { ...input, util: hostUtil };\n },\n workflowInput,\n };\n}\n\n/**\n * Reuses the current runtime-input group when every field of the freshly\n * constructed `next` literal is reference-equal, so unchanged groups keep a\n * stable identity across runtime reads.\n */\nfunction reuseIfShallowEqual<T extends object>(\n current: T | undefined,\n next: T,\n): T {\n if (!current) {\n return next;\n }\n for (const key of Object.keys(next)) {\n if (Reflect.get(current, key) !== Reflect.get(next, key)) {\n return next;\n }\n }\n return current;\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type VerifiedPrincipalPolicyCurrent } from "../../internal/crypto/index.js";
|
|
2
|
+
import { type ExecSql } from "../sqlite/sqlSchema.js";
|
|
3
|
+
import { type PrincipalHistoryEvidencePage } from "./principalHistoryEvidencePersistence.js";
|
|
4
|
+
import type { PrincipalHistoryPrefix } from "./principalHistoryPrefixPersistence.js";
|
|
5
|
+
import type { PrincipalHistoryStage } from "./principalHistoryStagePersistence.js";
|
|
6
|
+
/** Owned output of authenticated prefix extension and exact receipt verification. */
|
|
7
|
+
export interface AcknowledgedPrincipalCurrentPublication {
|
|
8
|
+
readonly policy: VerifiedPrincipalPolicyCurrent;
|
|
9
|
+
readonly prefix: PrincipalHistoryPrefix;
|
|
10
|
+
readonly previousPrefixProgress: string;
|
|
11
|
+
readonly stage: PrincipalHistoryStage;
|
|
12
|
+
readonly predecessorStage: PrincipalHistoryStage;
|
|
13
|
+
readonly evidence: PrincipalHistoryEvidencePage;
|
|
14
|
+
}
|
|
15
|
+
export interface AcknowledgedPrincipalCurrentRetirement {
|
|
16
|
+
readonly principalId: string;
|
|
17
|
+
readonly principalType: VerifiedPrincipalPolicyCurrent["principalType"];
|
|
18
|
+
readonly containerIds: readonly string[];
|
|
19
|
+
}
|
|
20
|
+
/** Store current artifacts and resumable progress in the same transaction as their pins. */
|
|
21
|
+
export declare function persistAcknowledgedPrincipalCurrents(input: {
|
|
22
|
+
readonly execSql: ExecSql;
|
|
23
|
+
readonly organizationId: string;
|
|
24
|
+
readonly entries: readonly AcknowledgedPrincipalCurrentPublication[];
|
|
25
|
+
readonly retirements?: readonly AcknowledgedPrincipalCurrentRetirement[] | undefined;
|
|
26
|
+
readonly stillCurrent: () => boolean;
|
|
27
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { KeyingVerificationError, verifyPrincipalPolicyCheckpoint, } from "../../internal/crypto/index.js";
|
|
2
|
+
import { eq } from "drizzle-orm";
|
|
3
|
+
import { assertProjectionVerificationCurrent } from "../keyingProjectionVerification/types.js";
|
|
4
|
+
import { principalHistoryPrefixes } from "../sqlite/principalHistoryEvidenceSchema.js";
|
|
5
|
+
import { principalHistoryStages } from "../sqlite/principalHistoryStageSchema.js";
|
|
6
|
+
import { keyingCheckpointTables, principalPolicyTables, } from "../sqlite/schema.js";
|
|
7
|
+
import { getClientSQLitePersistenceRuntime, } from "../sqlite/sqlitePersistenceRuntime.js";
|
|
8
|
+
import { ensureSqlTables } from "../sqlite/sqlSchema.js";
|
|
9
|
+
import { loadStoredPrincipalPolicyCheckpoint, principalPolicyKey, upsertPrincipalPolicyCheckpointInTransaction, } from "./keyingCheckpointPersistence.js";
|
|
10
|
+
import { storePrincipalGrantRetirements } from "./principalGrantRetirementPersistence.js";
|
|
11
|
+
import { writePrincipalHistoryEvidencePage, } from "./principalHistoryEvidencePersistence.js";
|
|
12
|
+
function assertPublicationScope(entries, organizationId) {
|
|
13
|
+
const seen = new Set();
|
|
14
|
+
for (const entry of entries) {
|
|
15
|
+
const key = principalPolicyKey(entry.policy);
|
|
16
|
+
if (seen.has(key))
|
|
17
|
+
throw new KeyingVerificationError("duplicate_entry", "Acknowledged current batch repeats a principal");
|
|
18
|
+
seen.add(key);
|
|
19
|
+
if (entry.prefix.organizationId !== organizationId ||
|
|
20
|
+
entry.stage.organizationId !== organizationId ||
|
|
21
|
+
entry.predecessorStage.organizationId !== organizationId ||
|
|
22
|
+
!entry.predecessorStage.complete ||
|
|
23
|
+
entry.predecessorStage.afterVersion !== entry.policy.version - 2 ||
|
|
24
|
+
entry.evidence.organizationId !== organizationId ||
|
|
25
|
+
entry.prefix.scopeId !== entry.evidence.scopeId ||
|
|
26
|
+
entry.prefix.version !== entry.policy.version ||
|
|
27
|
+
entry.stage.afterVersion !== entry.policy.version - 1 ||
|
|
28
|
+
!entry.stage.complete ||
|
|
29
|
+
entry.stage.currentJson !== entry.prefix.currentJson)
|
|
30
|
+
throw new KeyingVerificationError("object_mismatch", "Acknowledged current publication is outside its scope");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function validatePublication(tx, entry) {
|
|
34
|
+
const checkpoint = await loadStoredPrincipalPolicyCheckpoint(tx, entry.policy);
|
|
35
|
+
verifyPrincipalPolicyCheckpoint({
|
|
36
|
+
chain: entry.policy.retainedHistory,
|
|
37
|
+
currentState: entry.policy.state,
|
|
38
|
+
localCheckpoint: checkpoint,
|
|
39
|
+
});
|
|
40
|
+
if (!checkpoint || entry.policy.version > checkpoint.version + 1)
|
|
41
|
+
throw new KeyingVerificationError("stale_predecessor", "Acknowledged current policy must directly extend its durable checkpoint");
|
|
42
|
+
const [prefix] = await tx
|
|
43
|
+
.select()
|
|
44
|
+
.from(principalHistoryPrefixes)
|
|
45
|
+
.where(eq(principalHistoryPrefixes.scopeId, entry.prefix.scopeId))
|
|
46
|
+
.limit(1);
|
|
47
|
+
if (prefix?.progress !== entry.previousPrefixProgress)
|
|
48
|
+
throw new KeyingVerificationError("stale_predecessor", "Authenticated principal prefix changed before acknowledgement");
|
|
49
|
+
}
|
|
50
|
+
/** Store current artifacts and resumable progress in the same transaction as their pins. */
|
|
51
|
+
export async function persistAcknowledgedPrincipalCurrents(input) {
|
|
52
|
+
const entries = structuredClone(input.entries);
|
|
53
|
+
const retirements = structuredClone(input.retirements ?? []);
|
|
54
|
+
const organizationId = input.organizationId;
|
|
55
|
+
const stillCurrent = input.stillCurrent;
|
|
56
|
+
assertPublicationScope(entries, organizationId);
|
|
57
|
+
assertProjectionVerificationCurrent(stillCurrent);
|
|
58
|
+
await ensureSqlTables(input.execSql, [
|
|
59
|
+
...principalPolicyTables,
|
|
60
|
+
...keyingCheckpointTables,
|
|
61
|
+
]);
|
|
62
|
+
const runtime = getClientSQLitePersistenceRuntime(input.execSql);
|
|
63
|
+
const stored = await runtime.guardedTransaction(async (tx) => {
|
|
64
|
+
for (const entry of entries)
|
|
65
|
+
await validatePublication(tx, entry);
|
|
66
|
+
for (const retirement of retirements) {
|
|
67
|
+
const policy = entries.find((entry) => entry.policy.principalId === retirement.principalId &&
|
|
68
|
+
entry.policy.principalType === retirement.principalType)?.policy;
|
|
69
|
+
if (!policy)
|
|
70
|
+
throw new KeyingVerificationError("object_mismatch", "Retirement must accompany its acknowledged principal");
|
|
71
|
+
await storePrincipalGrantRetirements(tx, { ...retirement, policy, organizationId }, entries.map((entry) => entry.policy), organizationId);
|
|
72
|
+
}
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
await writePrincipalHistoryEvidencePage(tx, entry.evidence);
|
|
75
|
+
// A prefix-only predecessor must keep its encrypted key envelopes
|
|
76
|
+
// before its reusable slot is replaced by the acknowledged successor.
|
|
77
|
+
await tx
|
|
78
|
+
.insert(principalHistoryStages)
|
|
79
|
+
.values(entry.predecessorStage)
|
|
80
|
+
.onConflictDoUpdate({
|
|
81
|
+
target: principalHistoryStages.id,
|
|
82
|
+
set: entry.predecessorStage,
|
|
83
|
+
setWhere: eq(principalHistoryStages.complete, false),
|
|
84
|
+
})
|
|
85
|
+
.run();
|
|
86
|
+
// A fully authenticated publication supersedes an in-flight stage for
|
|
87
|
+
// this exact head. Its other writer fails its progress CAS and resumes.
|
|
88
|
+
await tx
|
|
89
|
+
.insert(principalHistoryStages)
|
|
90
|
+
.values(entry.stage)
|
|
91
|
+
.onConflictDoUpdate({
|
|
92
|
+
target: principalHistoryStages.id,
|
|
93
|
+
set: entry.stage,
|
|
94
|
+
})
|
|
95
|
+
.run();
|
|
96
|
+
await tx
|
|
97
|
+
.insert(principalHistoryPrefixes)
|
|
98
|
+
.values(entry.prefix)
|
|
99
|
+
.onConflictDoUpdate({
|
|
100
|
+
target: principalHistoryPrefixes.scopeId,
|
|
101
|
+
set: entry.prefix,
|
|
102
|
+
})
|
|
103
|
+
.run();
|
|
104
|
+
await upsertPrincipalPolicyCheckpointInTransaction(tx, entry.policy.checkpoint, new Date().toISOString(), organizationId);
|
|
105
|
+
}
|
|
106
|
+
}, stillCurrent, { behavior: "immediate" });
|
|
107
|
+
assertProjectionVerificationCurrent(() => stored.committed);
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=principalCurrentAcknowledgementPersistence.js.map
|
|
@@ -0,0 +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;\n readonly stage: PrincipalHistoryStage;\n readonly predecessorStage: PrincipalHistoryStage;\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.organizationId !== organizationId ||\n !entry.predecessorStage.complete ||\n entry.predecessorStage.afterVersion !== entry.policy.version - 2 ||\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 (!checkpoint || entry.policy.version > checkpoint.version + 1)\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 !== 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 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,9 +1,9 @@
|
|
|
1
|
-
import { type AccessManifestCheckpoint, type VerifiedPrincipalPolicy } from "../../internal/crypto/index.js";
|
|
1
|
+
import { type AccessManifestCheckpoint, type VerifiedPrincipalPolicy, type VerifiedPrincipalPolicyCurrent } from "../../internal/crypto/index.js";
|
|
2
2
|
import type { ClientSQLiteTransactionScope } from "../sqlite/sqlitePersistenceRuntime.js";
|
|
3
3
|
export interface AcknowledgedPrincipalGrantRetirements {
|
|
4
4
|
readonly containerIds: readonly string[];
|
|
5
5
|
readonly organizationId: string;
|
|
6
|
-
readonly policy: VerifiedPrincipalPolicy;
|
|
6
|
+
readonly policy: VerifiedPrincipalPolicy | VerifiedPrincipalPolicyCurrent;
|
|
7
7
|
}
|
|
8
|
-
export declare function storePrincipalGrantRetirements(tx: ClientSQLiteTransactionScope, retirement: AcknowledgedPrincipalGrantRetirements, policies: readonly VerifiedPrincipalPolicy[], organizationId: string | undefined): Promise<void>;
|
|
8
|
+
export declare function storePrincipalGrantRetirements(tx: ClientSQLiteTransactionScope, retirement: AcknowledgedPrincipalGrantRetirements, policies: readonly (VerifiedPrincipalPolicy | VerifiedPrincipalPolicyCurrent)[], organizationId: string | undefined): Promise<void>;
|
|
9
9
|
export declare function assertContainerNotRetired(tx: ClientSQLiteTransactionScope, checkpoint: AccessManifestCheckpoint): Promise<void>;
|
|
@@ -7,7 +7,10 @@ export async function storePrincipalGrantRetirements(tx, retirement, policies, o
|
|
|
7
7
|
policy.stateHash === retirement.policy.stateHash)) {
|
|
8
8
|
throw new KeyingVerificationError("object_mismatch", "Retirement must accompany its acknowledged policy");
|
|
9
9
|
}
|
|
10
|
-
const
|
|
10
|
+
const history = "retainedHistory" in retirement.policy
|
|
11
|
+
? retirement.policy.retainedHistory
|
|
12
|
+
: retirement.policy.history;
|
|
13
|
+
const previous = history?.find((entry) => entry.state.stateHash === retirement.policy.state.prevStateHash);
|
|
11
14
|
const granted = new Set([...retirement.policy.grants, ...(previous?.grants ?? [])].map((grant) => grant.containerId));
|
|
12
15
|
if (retirement.organizationId !== organizationId ||
|
|
13
16
|
retirement.containerIds.some((id) => !granted.has(id))) {
|