@tearleads/client-sdk 0.1.11 → 0.1.13
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/README.md +18 -2
- package/client/Tearleads.d.ts +4 -0
- package/client/Tearleads.js +5 -0
- package/client/Tearleads.js.map +1 -1
- package/client/index.d.ts +1 -0
- package/client/index.js.map +1 -1
- package/client/principalHistoryProtection.d.ts +19 -0
- package/client/principalHistoryProtection.js +76 -0
- package/client/principalHistoryProtection.js.map +1 -0
- package/client/workflowRuntime.d.ts +5 -0
- package/client/workflowRuntime.js +34 -5
- package/client/workflowRuntime.js.map +1 -1
- package/data/containers/shared/authorAccess.d.ts +2 -2
- package/data/containers/shared/authorAccess.js.map +1 -1
- package/data/containers/shared/heldContainerHeads.js.map +1 -1
- package/data/containers/shared/principalPolicies.js.map +1 -1
- package/data/containers/shared/types.d.ts +3 -2
- package/data/containers/shared/types.js.map +1 -1
- package/data/keyingProjectionVerification/accessManifestCheckpointEnforcement.js.map +1 -1
- package/data/keyingProjectionVerification/checkpointContext.d.ts +4 -3
- package/data/keyingProjectionVerification/checkpointContext.js.map +1 -1
- package/data/keyingProjectionVerification/containerManifestVerification.js.map +1 -1
- package/data/keyingProjectionVerification/containerPathVerification.js.map +1 -1
- package/data/keyingProjectionVerification/containerProjectionVerification.d.ts +4 -3
- package/data/keyingProjectionVerification/containerProjectionVerification.js.map +1 -1
- package/data/keyingProjectionVerification/documentManifestPolicies.js.map +1 -1
- package/data/keyingProjectionVerification/documentManifestVerification.js.map +1 -1
- package/data/keyingProjectionVerification/documentProjectionVerification.d.ts +3 -2
- package/data/keyingProjectionVerification/documentProjectionVerification.js.map +1 -1
- package/data/keyingProjectionVerification/organizationFounderBinding.js +9 -4
- package/data/keyingProjectionVerification/organizationFounderBinding.js.map +1 -1
- package/data/keyingProjectionVerification/principalPolicyCache.js +3 -4
- package/data/keyingProjectionVerification/principalPolicyCache.js.map +1 -1
- package/data/keyingProjectionVerification/principalPolicyVerification.js +12 -2
- package/data/keyingProjectionVerification/principalPolicyVerification.js.map +1 -1
- package/data/keyingProjectionVerification/projectionAuthorizationEvidence.js.map +1 -1
- package/data/keyingProjectionVerification/resolvedPrincipalPolicy.js +70 -0
- package/data/keyingProjectionVerification/resolvedPrincipalPolicy.js.map +1 -0
- package/data/keyingProjectionVerification/types.d.ts +17 -2
- package/data/keyingProjectionVerification/types.js +12 -0
- package/data/keyingProjectionVerification/types.js.map +1 -1
- package/data/persistence/organizationFounderPersistence.js +3 -2
- package/data/persistence/organizationFounderPersistence.js.map +1 -1
- package/data/persistence/principalHistoryEvidencePersistence.js +91 -0
- package/data/persistence/principalHistoryEvidencePersistence.js.map +1 -0
- package/data/persistence/principalHistoryPrefixPersistence.js +52 -0
- package/data/persistence/principalHistoryPrefixPersistence.js.map +1 -0
- package/data/persistence/principalHistoryStagePersistence.js +9 -2
- package/data/persistence/principalHistoryStagePersistence.js.map +1 -1
- package/data/persistence/principalKeyEnvelopeCandidates.js +67 -0
- package/data/persistence/principalKeyEnvelopeCandidates.js.map +1 -0
- package/data/principals/principalHistoryPrefixProtection.js +29 -0
- package/data/principals/principalHistoryPrefixProtection.js.map +1 -0
- package/data/principals/principalHistoryProtection.d.ts +10 -0
- package/data/principals/principalHistoryProtection.js +14 -0
- package/data/principals/principalHistoryProtection.js.map +1 -0
- package/data/principals/principalHistoryStageProtection.js.map +1 -1
- package/data/principals/principalPolicyCrypto.js +11 -21
- package/data/principals/principalPolicyCrypto.js.map +1 -1
- package/data/principals/principalPolicyEvidence.d.ts +4 -0
- package/data/principals/principalPolicyEvidence.js +12 -0
- package/data/principals/principalPolicyEvidence.js.map +1 -0
- package/data/sqlite/principalHistoryEvidenceSchema.js +46 -0
- package/data/sqlite/principalHistoryEvidenceSchema.js.map +1 -0
- package/data/sqlite/principalKeyFingerprintJson.js +9 -0
- package/data/sqlite/principalKeyFingerprintJson.js.map +1 -0
- package/data/sqlite/principalPolicySchema.js +4 -0
- package/data/sqlite/principalPolicySchema.js.map +1 -1
- package/data/sqlite/schema.d.ts +304 -0
- package/data/sqlite/schema.js +5 -0
- package/data/sqlite/schema.js.map +1 -1
- package/data/sqlite/sqlTableSchema.js +1 -1
- package/data/sqlite/sqlTableSchema.js.map +1 -1
- package/index.d.ts +3 -3
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/internal/api-client/principalPolicyPages.d.ts +2 -0
- package/internal/api-client/principalPolicyPages.js +11 -2
- package/internal/api-client/principalPolicyPages.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 +1 -1
- package/internal/crypto/keying/index.js +1 -1
- package/internal/crypto/keying/index.js.map +1 -1
- package/internal/crypto/keying/principalPolicyHistoryIndex.d.ts +6 -2
- package/internal/crypto/keying/principalPolicyHistoryIndex.js +11 -5
- package/internal/crypto/keying/principalPolicyHistoryIndex.js.map +1 -1
- package/internal/crypto/keying/principalPolicyHistoryReferences.d.ts +2 -0
- package/internal/crypto/keying/principalPolicyHistoryReferences.js +19 -2
- package/internal/crypto/keying/principalPolicyHistoryReferences.js.map +1 -1
- package/package.json +1 -1
- package/workflows/containers/child/create.js.map +1 -1
- package/workflows/containers/child/move.js.map +1 -1
- package/workflows/containers/child/moveWraps.js.map +1 -1
- package/workflows/containers/child/mutationRequestCore.js.map +1 -1
- package/workflows/containers/child/recitePlan.js.map +1 -1
- package/workflows/containers/child/rekey.d.ts +3 -2
- package/workflows/containers/child/rekey.js.map +1 -1
- package/workflows/containers/child/revoke.d.ts +2 -2
- package/workflows/containers/child/revoke.js.map +1 -1
- package/workflows/containers/child/rotationPrincipalPolicies.js.map +1 -1
- package/workflows/containers/child/rotationWraps.js.map +1 -1
- package/workflows/containers/child/sharePlanCore.d.ts +11 -10
- package/workflows/containers/child/sharePlanCore.js.map +1 -1
- package/workflows/documents/linkSetAuthority.js.map +1 -1
- package/workflows/principals/index.d.ts +1 -0
- package/workflows/principals/index.js +1 -0
- package/workflows/principals/index.js.map +1 -1
- package/workflows/principals/principalHistoryRecoveryPrefix.js +65 -0
- package/workflows/principals/principalHistoryRecoveryPrefix.js.map +1 -0
- package/workflows/principals/principalHistoryRecoveryReferences.js +60 -0
- package/workflows/principals/principalHistoryRecoveryReferences.js.map +1 -0
- package/workflows/principals/principalHistoryRecoveryStage.js +38 -2
- package/workflows/principals/principalHistoryRecoveryStage.js.map +1 -1
- package/workflows/principals/principalHistoryRecoveryTypes.d.ts +9 -1
- package/workflows/principals/principalHistoryRecoveryTypes.js +7 -0
- package/workflows/principals/principalHistoryRecoveryTypes.js.map +1 -1
- package/workflows/principals/principalHistoryRecoveryVerification.js +24 -0
- package/workflows/principals/principalHistoryRecoveryVerification.js.map +1 -0
- package/workflows/principals/principalRecoveryDirectory.d.ts +10 -0
- package/workflows/principals/principalRecoveryDirectory.js +107 -0
- package/workflows/principals/principalRecoveryDirectory.js.map +1 -0
- package/workflows/principals/principalRecoveryMemo.js +46 -0
- package/workflows/principals/principalRecoveryMemo.js.map +1 -0
- package/workflows/principals/principalRecoveryOutage.js +40 -0
- package/workflows/principals/principalRecoveryOutage.js.map +1 -0
- package/workflows/principals/principalRecoveryQueue.js +22 -0
- package/workflows/principals/principalRecoveryQueue.js.map +1 -0
- package/workflows/principals/recoverPrincipalPolicyHistory.d.ts +1 -0
- package/workflows/principals/recoverPrincipalPolicyHistory.js +108 -36
- package/workflows/principals/recoverPrincipalPolicyHistory.js.map +1 -1
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.d.ts +13 -0
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.js +150 -0
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.js.map +1 -0
- package/workflows/principals/runtimePolicyRecovery.js +65 -0
- package/workflows/principals/runtimePolicyRecovery.js.map +1 -0
- package/workflows/principals/runtimePolicyWarmer.js +5 -1
- package/workflows/principals/runtimePolicyWarmer.js.map +1 -1
- package/workflows/sync/remoteResetPrincipalScope.js +10 -0
- package/workflows/sync/remoteResetPrincipalScope.js.map +1 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { KeyingVerificationError, serializeKeyingCanonicalJson, verifyPrincipalPolicyHistoryReferences, } from "../../internal/crypto/index.js";
|
|
2
|
+
import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
|
|
3
|
+
import { loadPrincipalHistoryReference } from "../../data/persistence/principalHistoryEvidencePersistence.js";
|
|
4
|
+
import { parseOrganizationAuthorityDescriptor, } from "../../data/principals/organizationAuthorityDescriptor.js";
|
|
5
|
+
import { principalHistoryEvidenceScopeId } from "../../data/principals/principalHistoryPrefixProtection.js";
|
|
6
|
+
import { loadVerifiedPrincipalHistoryPrefix } from "./principalHistoryRecoveryPrefix.js";
|
|
7
|
+
import { PrincipalPolicyHistoryReadError, } from "./principalHistoryRecoveryTypes.js";
|
|
8
|
+
import { recoverPrincipalPolicyHistory } from "./recoverPrincipalPolicyHistory.js";
|
|
9
|
+
export class PrincipalRecoveryDirectoryAdvanced extends Error {
|
|
10
|
+
}
|
|
11
|
+
/** An unverified discovery read chooses a pin; recovery must authenticate it. */
|
|
12
|
+
async function discoverDirectoryHead(input) {
|
|
13
|
+
assertProjectionVerificationCurrent(() => !input.signal?.aborted && input.stillCurrent());
|
|
14
|
+
if (input.offline) {
|
|
15
|
+
const principal = {
|
|
16
|
+
principalType: "organization",
|
|
17
|
+
principalId: input.organizationId,
|
|
18
|
+
};
|
|
19
|
+
const scopeId = await principalHistoryEvidenceScopeId({
|
|
20
|
+
organizationId: input.organizationId,
|
|
21
|
+
head: principal,
|
|
22
|
+
protection: input.protection,
|
|
23
|
+
});
|
|
24
|
+
const prefix = await loadVerifiedPrincipalHistoryPrefix(input, principal, scopeId);
|
|
25
|
+
if (!prefix)
|
|
26
|
+
throw new KeyingVerificationError("missing_dependency", "Organization directory is unavailable offline");
|
|
27
|
+
const history = prefix.verifier.finish(prefix.head);
|
|
28
|
+
if (!history.ok)
|
|
29
|
+
throw history.error;
|
|
30
|
+
const genesis = await loadPrincipalHistoryReference({
|
|
31
|
+
execSql: input.execSql,
|
|
32
|
+
scopeId,
|
|
33
|
+
history: history.value,
|
|
34
|
+
version: 1,
|
|
35
|
+
});
|
|
36
|
+
const selected = await verifyPrincipalPolicyHistoryReferences({
|
|
37
|
+
history: history.value,
|
|
38
|
+
references: [genesis],
|
|
39
|
+
});
|
|
40
|
+
if (!selected.ok)
|
|
41
|
+
throw selected.error;
|
|
42
|
+
return { head: prefix.head, genesis: genesis.reference };
|
|
43
|
+
}
|
|
44
|
+
for await (const result of input.apiClient.getPrincipalPolicyPages("organization", input.organizationId, { signal: input.signal })) {
|
|
45
|
+
if (!result.ok)
|
|
46
|
+
throw new PrincipalPolicyHistoryReadError(result);
|
|
47
|
+
assertProjectionVerificationCurrent(() => !input.signal?.aborted && input.stillCurrent());
|
|
48
|
+
const state = result.data.currentState;
|
|
49
|
+
const first = result.data.previousStates[0]?.state ?? state;
|
|
50
|
+
if (first.version !== 1 ||
|
|
51
|
+
first.principalType !== "organization" ||
|
|
52
|
+
first.principalId !== input.organizationId)
|
|
53
|
+
throw new KeyingVerificationError("missing_dependency", "Organization directory discovery requires genesis");
|
|
54
|
+
return {
|
|
55
|
+
genesis: {
|
|
56
|
+
principalType: first.principalType,
|
|
57
|
+
principalId: first.principalId,
|
|
58
|
+
version: first.version,
|
|
59
|
+
stateHash: first.stateHash,
|
|
60
|
+
keyEpoch: first.keyEpoch,
|
|
61
|
+
keyFingerprint: first.keyFingerprint,
|
|
62
|
+
},
|
|
63
|
+
head: {
|
|
64
|
+
principalType: "organization",
|
|
65
|
+
principalId: input.organizationId,
|
|
66
|
+
version: state.version,
|
|
67
|
+
stateHash: state.stateHash,
|
|
68
|
+
keyEpoch: state.keyEpoch,
|
|
69
|
+
keyFingerprint: state.keyFingerprint,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
throw new KeyingVerificationError("missing_dependency", "Organization directory head is unavailable");
|
|
74
|
+
}
|
|
75
|
+
export async function recoverPolicyDirectory(input, references) {
|
|
76
|
+
const scoped = {
|
|
77
|
+
...input,
|
|
78
|
+
protection: {
|
|
79
|
+
localKey: input.protection.localKey,
|
|
80
|
+
context: serializeKeyingCanonicalJson([
|
|
81
|
+
"tearleads.sdk.principal-history.directory.v1",
|
|
82
|
+
input.protection.context,
|
|
83
|
+
]),
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
const { head: expectedHead, genesis } = await discoverDirectoryHead(scoped);
|
|
87
|
+
if (references.some((reference) => reference.version > expectedHead.version))
|
|
88
|
+
throw new PrincipalRecoveryDirectoryAdvanced();
|
|
89
|
+
const recovered = await recoverPrincipalPolicyHistory({
|
|
90
|
+
...scoped,
|
|
91
|
+
expectedHead,
|
|
92
|
+
retainedReferences: references.some((reference) => reference.version === 1)
|
|
93
|
+
? references
|
|
94
|
+
: [genesis, ...references],
|
|
95
|
+
});
|
|
96
|
+
let descriptor;
|
|
97
|
+
try {
|
|
98
|
+
descriptor = parseOrganizationAuthorityDescriptor(recovered.current.currentPayload.ciphertext);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
throw new KeyingVerificationError("invalid_shape", "Organization directory payload is invalid");
|
|
102
|
+
}
|
|
103
|
+
if (descriptor.organizationId !== input.organizationId)
|
|
104
|
+
throw new KeyingVerificationError("object_mismatch", "Organization directory scope does not match");
|
|
105
|
+
return { ...recovered, descriptor };
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=principalRecoveryDirectory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principalRecoveryDirectory.js","sourceRoot":"","sources":["principalRecoveryDirectory.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type ReferencedPrincipalHead,\n serializeKeyingCanonicalJson,\n verifyPrincipalPolicyHistoryReferences,\n} from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { loadPrincipalHistoryReference } from \"../../data/persistence/principalHistoryEvidencePersistence\";\nimport {\n type OrganizationAuthorityDescriptor,\n parseOrganizationAuthorityDescriptor,\n} from \"../../data/principals/organizationAuthorityDescriptor\";\nimport { principalHistoryEvidenceScopeId } from \"../../data/principals/principalHistoryPrefixProtection\";\nimport { loadVerifiedPrincipalHistoryPrefix } from \"./principalHistoryRecoveryPrefix\";\nimport {\n PrincipalPolicyHistoryReadError,\n type RecoveredPrincipalPolicyHistory,\n type RecoverPrincipalPolicyHistoryOptions,\n} from \"./principalHistoryRecoveryTypes\";\nimport { recoverPrincipalPolicyHistory } from \"./recoverPrincipalPolicyHistory\";\n\nexport type PrincipalRecoveryContext = Omit<\n RecoverPrincipalPolicyHistoryOptions,\n | \"expectedHead\"\n | \"retainedReferences\"\n | \"historyVerification\"\n | \"loadExternalAuthority\"\n>;\n\nexport interface RecoveredPolicyDirectory\n extends RecoveredPrincipalPolicyHistory {\n readonly descriptor: OrganizationAuthorityDescriptor;\n}\n\nexport class PrincipalRecoveryDirectoryAdvanced extends Error {}\n\n/** An unverified discovery read chooses a pin; recovery must authenticate it. */\nasync function discoverDirectoryHead(input: PrincipalRecoveryContext): Promise<{\n head: ReferencedPrincipalHead;\n genesis: ReferencedPrincipalHead;\n}> {\n assertProjectionVerificationCurrent(\n () => !input.signal?.aborted && input.stillCurrent(),\n );\n if (input.offline) {\n const principal = {\n principalType: \"organization\" as const,\n principalId: input.organizationId,\n };\n const scopeId = await principalHistoryEvidenceScopeId({\n organizationId: input.organizationId,\n head: principal,\n protection: input.protection,\n });\n const prefix = await loadVerifiedPrincipalHistoryPrefix(\n input,\n principal,\n scopeId,\n );\n if (!prefix)\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Organization directory is unavailable offline\",\n );\n const history = prefix.verifier.finish(prefix.head);\n if (!history.ok) throw history.error;\n const genesis = await loadPrincipalHistoryReference({\n execSql: input.execSql,\n scopeId,\n history: history.value,\n version: 1,\n });\n const selected = await verifyPrincipalPolicyHistoryReferences({\n history: history.value,\n references: [genesis],\n });\n if (!selected.ok) throw selected.error;\n return { head: prefix.head, genesis: genesis.reference };\n }\n for await (const result of input.apiClient.getPrincipalPolicyPages(\n \"organization\",\n input.organizationId,\n { signal: input.signal },\n )) {\n if (!result.ok) throw new PrincipalPolicyHistoryReadError(result);\n assertProjectionVerificationCurrent(\n () => !input.signal?.aborted && input.stillCurrent(),\n );\n const state = result.data.currentState;\n const first = result.data.previousStates[0]?.state ?? state;\n if (\n first.version !== 1 ||\n first.principalType !== \"organization\" ||\n first.principalId !== input.organizationId\n )\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Organization directory discovery requires genesis\",\n );\n return {\n genesis: {\n principalType: first.principalType,\n principalId: first.principalId,\n version: first.version,\n stateHash: first.stateHash,\n keyEpoch: first.keyEpoch,\n keyFingerprint: first.keyFingerprint,\n },\n head: {\n principalType: \"organization\",\n principalId: input.organizationId,\n version: state.version,\n stateHash: state.stateHash,\n keyEpoch: state.keyEpoch,\n keyFingerprint: state.keyFingerprint,\n },\n };\n }\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Organization directory head is unavailable\",\n );\n}\n\nexport async function recoverPolicyDirectory(\n input: PrincipalRecoveryContext,\n references: readonly ReferencedPrincipalHead[],\n): Promise<RecoveredPolicyDirectory> {\n const scoped = {\n ...input,\n protection: {\n localKey: input.protection.localKey,\n context: serializeKeyingCanonicalJson([\n \"tearleads.sdk.principal-history.directory.v1\",\n input.protection.context,\n ]),\n },\n };\n const { head: expectedHead, genesis } = await discoverDirectoryHead(scoped);\n if (references.some((reference) => reference.version > expectedHead.version))\n throw new PrincipalRecoveryDirectoryAdvanced();\n const recovered = await recoverPrincipalPolicyHistory({\n ...scoped,\n expectedHead,\n retainedReferences: references.some((reference) => reference.version === 1)\n ? references\n : [genesis, ...references],\n });\n let descriptor: OrganizationAuthorityDescriptor;\n try {\n descriptor = parseOrganizationAuthorityDescriptor(\n recovered.current.currentPayload.ciphertext,\n );\n } catch {\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"Organization directory payload is invalid\",\n );\n }\n if (descriptor.organizationId !== input.organizationId)\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Organization directory scope does not match\",\n );\n return { ...recovered, descriptor };\n}\n"]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { serializeKeyingCanonicalJson, } from "../../internal/crypto/index.js";
|
|
2
|
+
import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
|
|
3
|
+
import { recoverPolicyDirectory, } from "./principalRecoveryDirectory.js";
|
|
4
|
+
import { recoverPrincipalPolicyHistory } from "./recoverPrincipalPolicyHistory.js";
|
|
5
|
+
/** Only share authenticated results within one projection collection lifetime. */
|
|
6
|
+
async function memoPrincipalRecovery(memo, key, stillCurrent, work) {
|
|
7
|
+
assertProjectionVerificationCurrent(stillCurrent);
|
|
8
|
+
let pending = memo?.get(key);
|
|
9
|
+
if (!pending) {
|
|
10
|
+
pending = work().then((value) => ({ value, stillCurrent }));
|
|
11
|
+
memo?.set(key, pending);
|
|
12
|
+
}
|
|
13
|
+
const result = await pending.catch((error) => {
|
|
14
|
+
if (memo?.get(key) === pending)
|
|
15
|
+
memo.delete(key);
|
|
16
|
+
throw error;
|
|
17
|
+
});
|
|
18
|
+
assertProjectionVerificationCurrent(result.stillCurrent);
|
|
19
|
+
assertProjectionVerificationCurrent(stillCurrent);
|
|
20
|
+
return result.value;
|
|
21
|
+
}
|
|
22
|
+
export function createPrincipalRecoveryReader(input, memo) {
|
|
23
|
+
const scope = [
|
|
24
|
+
input.organizationId,
|
|
25
|
+
input.protection.context,
|
|
26
|
+
input.offline === true,
|
|
27
|
+
];
|
|
28
|
+
const current = () => !input.signal?.aborted && input.stillCurrent();
|
|
29
|
+
return {
|
|
30
|
+
directory: (references) => memoPrincipalRecovery(memo?.directories, serializeKeyingCanonicalJson([
|
|
31
|
+
...scope,
|
|
32
|
+
references.map((reference) => ({ ...reference })),
|
|
33
|
+
]), current, () => recoverPolicyDirectory(input, references)),
|
|
34
|
+
admins: (expectedHead, retainedReferences = []) => memoPrincipalRecovery(memo?.admins, serializeKeyingCanonicalJson([
|
|
35
|
+
...scope,
|
|
36
|
+
{ ...expectedHead },
|
|
37
|
+
retainedReferences.map((reference) => ({ ...reference })),
|
|
38
|
+
]), current, () => recoverPrincipalPolicyHistory({
|
|
39
|
+
...input,
|
|
40
|
+
expectedHead,
|
|
41
|
+
retainedReferences,
|
|
42
|
+
historyVerification: "direct-admins",
|
|
43
|
+
})),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=principalRecoveryMemo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principalRecoveryMemo.js","sourceRoot":"","sources":["principalRecoveryMemo.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AAEpG,OAAO,EAGL,sBAAsB,GACvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AAShF,kFAAkF;AAClF,KAAK,UAAU,qBAAqB,CAClC,IAAyB,EACzB,GAAW,EACX,YAA2B,EAC3B,IAAsB;IAEtB,mCAAmC,CAAC,YAAY,CAAC,CAAC;IAClD,IAAI,OAAO,GAAG,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;QAC5D,IAAI,EAAE,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC1B,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QACpD,IAAI,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,OAAO;YAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjD,MAAM,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;IACH,mCAAmC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACzD,mCAAmC,CAAC,YAAY,CAAC,CAAC;IAClD,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,6BAA6B,CAC3C,KAA+B,EAC/B,IAA4B;IAE5B,MAAM,KAAK,GAAG;QACZ,KAAK,CAAC,cAAc;QACpB,KAAK,CAAC,UAAU,CAAC,OAAO;QACxB,KAAK,CAAC,OAAO,KAAK,IAAI;KACvB,CAAC;IACF,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;IACrE,OAAO;QACL,SAAS,EAAE,CAAC,UAA8C,EAAE,EAAE,CAC5D,qBAAqB,CACnB,IAAI,EAAE,WAAW,EACjB,4BAA4B,CAAC;YAC3B,GAAG,KAAK;YACR,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,SAAS,EAAE,CAAC,CAAC;SAClD,CAAC,EACF,OAAO,EACP,GAAG,EAAE,CAAC,sBAAsB,CAAC,KAAK,EAAE,UAAU,CAAC,CAChD;QACH,MAAM,EAAE,CACN,YAAqC,EACrC,qBAAyD,EAAE,EAC3D,EAAE,CACF,qBAAqB,CACnB,IAAI,EAAE,MAAM,EACZ,4BAA4B,CAAC;YAC3B,GAAG,KAAK;YACR,EAAE,GAAG,YAAY,EAAE;YACnB,kBAAkB,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,SAAS,EAAE,CAAC,CAAC;SAC1D,CAAC,EACF,OAAO,EACP,GAAG,EAAE,CACH,6BAA6B,CAAC;YAC5B,GAAG,KAAK;YACR,YAAY;YACZ,kBAAkB;YAClB,mBAAmB,EAAE,eAAe;SACrC,CAAC,CACL;KACJ,CAAC;AACJ,CAAC","sourcesContent":["import {\n type ReferencedPrincipalHead,\n serializeKeyingCanonicalJson,\n} from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport type { RecoveredPrincipalPolicyHistory } from \"./principalHistoryRecoveryTypes\";\nimport {\n type PrincipalRecoveryContext,\n type RecoveredPolicyDirectory,\n recoverPolicyDirectory,\n} from \"./principalRecoveryDirectory\";\nimport { recoverPrincipalPolicyHistory } from \"./recoverPrincipalPolicyHistory\";\n\ntype Memo<T> = Map<string, Promise<{ value: T; stillCurrent: () => boolean }>>;\n\nexport interface PrincipalRecoveryMemo {\n readonly directories: Memo<RecoveredPolicyDirectory>;\n readonly admins: Memo<RecoveredPrincipalPolicyHistory>;\n}\n\n/** Only share authenticated results within one projection collection lifetime. */\nasync function memoPrincipalRecovery<T>(\n memo: Memo<T> | undefined,\n key: string,\n stillCurrent: () => boolean,\n work: () => Promise<T>,\n): Promise<T> {\n assertProjectionVerificationCurrent(stillCurrent);\n let pending = memo?.get(key);\n if (!pending) {\n pending = work().then((value) => ({ value, stillCurrent }));\n memo?.set(key, pending);\n }\n const result = await pending.catch((error: unknown) => {\n if (memo?.get(key) === pending) memo.delete(key);\n throw error;\n });\n assertProjectionVerificationCurrent(result.stillCurrent);\n assertProjectionVerificationCurrent(stillCurrent);\n return result.value;\n}\n\nexport function createPrincipalRecoveryReader(\n input: PrincipalRecoveryContext,\n memo?: PrincipalRecoveryMemo,\n) {\n const scope = [\n input.organizationId,\n input.protection.context,\n input.offline === true,\n ];\n const current = () => !input.signal?.aborted && input.stillCurrent();\n return {\n directory: (references: readonly ReferencedPrincipalHead[]) =>\n memoPrincipalRecovery(\n memo?.directories,\n serializeKeyingCanonicalJson([\n ...scope,\n references.map((reference) => ({ ...reference })),\n ]),\n current,\n () => recoverPolicyDirectory(input, references),\n ),\n admins: (\n expectedHead: ReferencedPrincipalHead,\n retainedReferences: readonly ReferencedPrincipalHead[] = [],\n ) =>\n memoPrincipalRecovery(\n memo?.admins,\n serializeKeyingCanonicalJson([\n ...scope,\n { ...expectedHead },\n retainedReferences.map((reference) => ({ ...reference })),\n ]),\n current,\n () =>\n recoverPrincipalPolicyHistory({\n ...input,\n expectedHead,\n retainedReferences,\n historyVerification: \"direct-admins\",\n }),\n ),\n };\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { KeyingVerificationError } from "../../internal/crypto/index.js";
|
|
2
|
+
import { ProjectionDependencyUnavailableError } from "../../data/keyingProjectionVerification/dependencyUnavailable.js";
|
|
3
|
+
import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
|
|
4
|
+
import { PrincipalPolicyHistoryReadError } from "./principalHistoryRecoveryTypes.js";
|
|
5
|
+
import { recoverScopedPrincipalPolicyHistory } from "./recoverScopedPrincipalPolicyHistory.js";
|
|
6
|
+
/** Refusals and invalid evidence must never silently select a cached policy. */
|
|
7
|
+
function isPrincipalRecoveryOutage(error) {
|
|
8
|
+
if (!(error instanceof PrincipalPolicyHistoryReadError))
|
|
9
|
+
return false;
|
|
10
|
+
const { failure } = error;
|
|
11
|
+
return (failure.kind === "network" ||
|
|
12
|
+
(failure.kind === "http" &&
|
|
13
|
+
failure.status !== null &&
|
|
14
|
+
failure.status >= 500) ||
|
|
15
|
+
(failure.kind === "cancelled" &&
|
|
16
|
+
failure.code === "principal_history_request_timed_out"));
|
|
17
|
+
}
|
|
18
|
+
export async function recoverWithPrincipalOutageFallback(recover, options) {
|
|
19
|
+
try {
|
|
20
|
+
return await recover(options);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
assertProjectionVerificationCurrent(options.stillCurrent);
|
|
24
|
+
if (options.offline || !isPrincipalRecoveryOutage(error))
|
|
25
|
+
throw error;
|
|
26
|
+
try {
|
|
27
|
+
return await recoverScopedPrincipalPolicyHistory({
|
|
28
|
+
...options,
|
|
29
|
+
offline: true,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (localError) {
|
|
33
|
+
if (localError instanceof KeyingVerificationError &&
|
|
34
|
+
localError.code === "missing_dependency")
|
|
35
|
+
throw new ProjectionDependencyUnavailableError(localError.message);
|
|
36
|
+
throw localError;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=principalRecoveryOutage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principalRecoveryOutage.js","sourceRoot":"","sources":["principalRecoveryOutage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,oCAAoC,EAAE,MAAM,+DAA+D,CAAC;AACrH,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,EAAE,mCAAmC,EAAE,MAAM,uCAAuC,CAAC;AAE5F,gFAAgF;AAChF,SAAS,yBAAyB,CAAC,KAAc;IAC/C,IAAI,CAAC,CAAC,KAAK,YAAY,+BAA+B,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC1B,OAAO,CACL,OAAO,CAAC,IAAI,KAAK,SAAS;QAC1B,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM;YACtB,OAAO,CAAC,MAAM,KAAK,IAAI;YACvB,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC;QACxB,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW;YAC3B,OAAO,CAAC,IAAI,KAAK,qCAAqC,CAAC,CAC1D,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kCAAkC,CACtD,OAAmD,EACnD,OAAkE;IAElE,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC1D,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC;QACtE,IAAI,CAAC;YACH,OAAO,MAAM,mCAAmC,CAAC;gBAC/C,GAAG,OAAO;gBACV,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,UAAU,EAAE,CAAC;YACpB,IACE,UAAU,YAAY,uBAAuB;gBAC7C,UAAU,CAAC,IAAI,KAAK,oBAAoB;gBAExC,MAAM,IAAI,oCAAoC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YACrE,MAAM,UAAU,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { ProjectionDependencyUnavailableError } from \"../../data/keyingProjectionVerification/dependencyUnavailable\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { PrincipalPolicyHistoryReadError } from \"./principalHistoryRecoveryTypes\";\nimport { recoverScopedPrincipalPolicyHistory } from \"./recoverScopedPrincipalPolicyHistory\";\n\n/** Refusals and invalid evidence must never silently select a cached policy. */\nfunction isPrincipalRecoveryOutage(error: unknown): boolean {\n if (!(error instanceof PrincipalPolicyHistoryReadError)) return false;\n const { failure } = error;\n return (\n failure.kind === \"network\" ||\n (failure.kind === \"http\" &&\n failure.status !== null &&\n failure.status >= 500) ||\n (failure.kind === \"cancelled\" &&\n failure.code === \"principal_history_request_timed_out\")\n );\n}\n\nexport async function recoverWithPrincipalOutageFallback(\n recover: typeof recoverScopedPrincipalPolicyHistory,\n options: Parameters<typeof recoverScopedPrincipalPolicyHistory>[0],\n) {\n try {\n return await recover(options);\n } catch (error) {\n assertProjectionVerificationCurrent(options.stillCurrent);\n if (options.offline || !isPrincipalRecoveryOutage(error)) throw error;\n try {\n return await recoverScopedPrincipalPolicyHistory({\n ...options,\n offline: true,\n });\n } catch (localError) {\n if (\n localError instanceof KeyingVerificationError &&\n localError.code === \"missing_dependency\"\n )\n throw new ProjectionDependencyUnavailableError(localError.message);\n throw localError;\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Directory and Admins stages are shared by every reference in an organization.
|
|
2
|
+
// Coordinate runtime callers across warmer instances; different databases and
|
|
3
|
+
// organizations keep independent queues. Completed queues retain no work.
|
|
4
|
+
// Queued work must not re-enter this queue for the same organization.
|
|
5
|
+
const queues = new WeakMap();
|
|
6
|
+
export function queuePrincipalRecovery(execSql, organizationId, work) {
|
|
7
|
+
let scoped = queues.get(execSql);
|
|
8
|
+
if (!scoped) {
|
|
9
|
+
scoped = new Map();
|
|
10
|
+
queues.set(execSql, scoped);
|
|
11
|
+
}
|
|
12
|
+
const operation = (scoped.get(organizationId) ?? Promise.resolve()).then(work);
|
|
13
|
+
const settled = operation.then(() => undefined, () => undefined);
|
|
14
|
+
scoped.set(organizationId, settled);
|
|
15
|
+
const owned = scoped;
|
|
16
|
+
void settled.then(() => {
|
|
17
|
+
if (owned.get(organizationId) === settled)
|
|
18
|
+
owned.delete(organizationId);
|
|
19
|
+
});
|
|
20
|
+
return operation;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=principalRecoveryQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principalRecoveryQueue.js","sourceRoot":"","sources":["principalRecoveryQueue.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,8EAA8E;AAC9E,0EAA0E;AAC1E,sEAAsE;AACtE,MAAM,MAAM,GAAG,IAAI,OAAO,EAAuC,CAAC;AAElE,MAAM,UAAU,sBAAsB,CACpC,OAAgB,EAChB,cAAsB,EACtB,IAAsB;IAEtB,IAAI,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;QACnB,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9B,CAAC;IACD,MAAM,SAAS,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CACtE,IAAI,CACL,CAAC;IACF,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAC5B,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CAChB,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,MAAM,CAAC;IACrB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE;QACrB,IAAI,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,OAAO;YAAE,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IACH,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["import type { ExecSql } from \"../../data/sqlite/sqlSchema\";\n\n// Directory and Admins stages are shared by every reference in an organization.\n// Coordinate runtime callers across warmer instances; different databases and\n// organizations keep independent queues. Completed queues retain no work.\n// Queued work must not re-enter this queue for the same organization.\nconst queues = new WeakMap<ExecSql, Map<string, Promise<void>>>();\n\nexport function queuePrincipalRecovery<T>(\n execSql: ExecSql,\n organizationId: string,\n work: () => Promise<T>,\n): Promise<T> {\n let scoped = queues.get(execSql);\n if (!scoped) {\n scoped = new Map();\n queues.set(execSql, scoped);\n }\n const operation = (scoped.get(organizationId) ?? Promise.resolve()).then(\n work,\n );\n const settled = operation.then(\n () => undefined,\n () => undefined,\n );\n scoped.set(organizationId, settled);\n const owned = scoped;\n void settled.then(() => {\n if (owned.get(organizationId) === settled) owned.delete(organizationId);\n });\n return operation;\n}\n"]}
|
|
@@ -2,5 +2,6 @@ import { type RecoveredPrincipalPolicyHistory, type RecoverPrincipalPolicyHistor
|
|
|
2
2
|
/**
|
|
3
3
|
* Recover one exact head with bounded retained history and durable checked progress.
|
|
4
4
|
* The result is a sparse current-policy capability; this does not advance app checkpoints.
|
|
5
|
+
* Retain organization version 1 when the result will establish founder binding.
|
|
5
6
|
*/
|
|
6
7
|
export declare function recoverPrincipalPolicyHistory(options: RecoverPrincipalPolicyHistoryOptions): Promise<RecoveredPrincipalPolicyHistory>;
|
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
import { KeyingVerificationError, verifyPrincipalPolicyCurrent, } from "../../internal/crypto/index.js";
|
|
1
|
+
import { createPrincipalPolicyHistoryVerifier, KeyingVerificationError, verifyPrincipalPolicyCheckpoint, verifyPrincipalPolicyCurrent, } from "../../internal/crypto/index.js";
|
|
2
2
|
import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
|
|
3
3
|
import { loadPrincipalPolicyCheckpoint } from "../../data/persistence/keyingCheckpointPersistence.js";
|
|
4
|
+
import { preparePrincipalHistoryEvidencePage } from "../../data/persistence/principalHistoryEvidencePersistence.js";
|
|
4
5
|
import { discardPrincipalHistoryStage, savePrincipalHistoryStage, } from "../../data/persistence/principalHistoryStagePersistence.js";
|
|
6
|
+
import { ownPrincipalHistoryProtection } from "../../data/principals/principalHistoryProtection.js";
|
|
5
7
|
import { principalHistoryStageProtection } from "../../data/principals/principalHistoryStageProtection.js";
|
|
6
8
|
import { collectPrincipalPolicySignerPublicKeys } from "./policyVerification.js";
|
|
9
|
+
import { publishReusablePrincipalHistoryPrefix } from "./principalHistoryRecoveryPrefix.js";
|
|
10
|
+
import { PrincipalHistoryEvidenceUnavailableError, selectRecoveredPrincipalHistory, } from "./principalHistoryRecoveryReferences.js";
|
|
7
11
|
import { restorePrincipalHistoryRecoveryStage, } from "./principalHistoryRecoveryStage.js";
|
|
8
|
-
import { PrincipalPolicyHistoryReadError, } from "./principalHistoryRecoveryTypes.js";
|
|
12
|
+
import { PrincipalHistoryRecoveryRaceError, PrincipalPolicyHistoryReadError, } from "./principalHistoryRecoveryTypes.js";
|
|
13
|
+
import { assertPrincipalHistoryVerificationMode, principalHistoryVerificationContext, } from "./principalHistoryRecoveryVerification.js";
|
|
14
|
+
class PrincipalHistoryPrefixDisconnectedError extends Error {
|
|
15
|
+
verificationError;
|
|
16
|
+
constructor(verificationError) {
|
|
17
|
+
super(verificationError.message);
|
|
18
|
+
this.verificationError = verificationError;
|
|
19
|
+
this.name = "PrincipalHistoryPrefixDisconnectedError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
9
22
|
function currentArtifacts(page) {
|
|
10
23
|
return {
|
|
11
24
|
currentState: page.currentState,
|
|
@@ -15,12 +28,13 @@ function currentArtifacts(page) {
|
|
|
15
28
|
currentMemberEnvelopes: page.currentMemberEnvelopes,
|
|
16
29
|
};
|
|
17
30
|
}
|
|
31
|
+
function recoveryIsCurrent(input) {
|
|
32
|
+
return !input.signal?.aborted && input.stillCurrent();
|
|
33
|
+
}
|
|
18
34
|
function assertCurrent(input) {
|
|
19
|
-
assertProjectionVerificationCurrent(() =>
|
|
35
|
+
assertProjectionVerificationCurrent(() => recoveryIsCurrent(input));
|
|
20
36
|
}
|
|
21
|
-
|
|
22
|
-
const page = structuredClone(response);
|
|
23
|
-
assertCurrent(input);
|
|
37
|
+
function assertExpectedHead(current, expectedHead) {
|
|
24
38
|
for (const field of [
|
|
25
39
|
"principalType",
|
|
26
40
|
"principalId",
|
|
@@ -29,13 +43,19 @@ async function acceptPage(input, stage, response) {
|
|
|
29
43
|
"keyEpoch",
|
|
30
44
|
"keyFingerprint",
|
|
31
45
|
]) {
|
|
32
|
-
if (
|
|
46
|
+
if (current.currentState[field] !== expectedHead[field])
|
|
33
47
|
throw new KeyingVerificationError("object_mismatch", "Principal history page does not match its requested head");
|
|
34
48
|
}
|
|
35
|
-
|
|
49
|
+
}
|
|
50
|
+
async function acceptPage(input, stage, response) {
|
|
51
|
+
const page = structuredClone(response);
|
|
52
|
+
assertCurrent(input);
|
|
53
|
+
assertExpectedHead(page, input.expectedHead);
|
|
54
|
+
if (stage.complete) {
|
|
36
55
|
if (page.previousStates.length !== 0 ||
|
|
37
56
|
page.historyPage.nextAfterVersion !== null)
|
|
38
57
|
throw new KeyingVerificationError("invalid_shape", "Completed history returned additional entries");
|
|
58
|
+
stage.current = currentArtifacts(page);
|
|
39
59
|
return;
|
|
40
60
|
}
|
|
41
61
|
const complete = page.historyPage.nextAfterVersion === null;
|
|
@@ -47,6 +67,7 @@ async function acceptPage(input, stage, response) {
|
|
|
47
67
|
projection: current.currentProjection,
|
|
48
68
|
grants: current.currentGrants,
|
|
49
69
|
});
|
|
70
|
+
assertPrincipalHistoryVerificationMode(input, entries);
|
|
50
71
|
const keys = await collectPrincipalPolicySignerPublicKeys({
|
|
51
72
|
bundle: { ...current, previousStates: page.previousStates },
|
|
52
73
|
resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
|
|
@@ -59,8 +80,11 @@ async function acceptPage(input, stage, response) {
|
|
|
59
80
|
signerPublicKeys: keys.signerPublicKeys,
|
|
60
81
|
...(externalAuthority ? { externalAuthority } : {}),
|
|
61
82
|
});
|
|
62
|
-
if (!appended.ok)
|
|
83
|
+
if (!appended.ok) {
|
|
84
|
+
if (appended.error.code === "stale_predecessor")
|
|
85
|
+
throw new PrincipalHistoryPrefixDisconnectedError(appended.error);
|
|
63
86
|
throw appended.error;
|
|
87
|
+
}
|
|
64
88
|
const next = {
|
|
65
89
|
id: stage.id,
|
|
66
90
|
organizationId: input.organizationId,
|
|
@@ -75,43 +99,59 @@ async function acceptPage(input, stage, response) {
|
|
|
75
99
|
await savePrincipalHistoryStage({
|
|
76
100
|
execSql: input.execSql,
|
|
77
101
|
stage: saved,
|
|
102
|
+
evidence: await preparePrincipalHistoryEvidencePage({
|
|
103
|
+
scopeId: stage.scopeId,
|
|
104
|
+
organizationId: input.organizationId,
|
|
105
|
+
entries,
|
|
106
|
+
nodes: appended.value.indexNodes,
|
|
107
|
+
}),
|
|
78
108
|
previousProgress: stage.saved?.progress ?? null,
|
|
79
|
-
stillCurrent: () =>
|
|
109
|
+
stillCurrent: () => recoveryIsCurrent(input),
|
|
80
110
|
});
|
|
81
111
|
stage.current = current;
|
|
82
112
|
stage.saved = saved;
|
|
113
|
+
stage.complete = complete;
|
|
83
114
|
}
|
|
84
115
|
async function finishRecovery(input, stage, checkpoint) {
|
|
85
|
-
if (!stage.current || !stage.
|
|
116
|
+
if (!stage.current || !stage.complete)
|
|
86
117
|
throw new KeyingVerificationError("missing_dependency", "Principal history ended before its pinned head");
|
|
87
118
|
const history = stage.verifier.finish(input.expectedHead);
|
|
88
119
|
if (!history.ok)
|
|
89
120
|
throw history.error;
|
|
121
|
+
const selected = await selectRecoveredPrincipalHistory({
|
|
122
|
+
options: input,
|
|
123
|
+
scopeId: stage.scopeId,
|
|
124
|
+
history: history.value,
|
|
125
|
+
checkpoint,
|
|
126
|
+
});
|
|
90
127
|
const current = await verifyPrincipalPolicyCurrent({
|
|
91
128
|
current: stage.current,
|
|
92
|
-
history:
|
|
129
|
+
history: selected,
|
|
93
130
|
});
|
|
94
131
|
if (!current.ok)
|
|
95
132
|
throw current.error;
|
|
133
|
+
verifyPrincipalPolicyCheckpoint({
|
|
134
|
+
chain: current.value.retainedHistory,
|
|
135
|
+
currentState: current.value.state,
|
|
136
|
+
localCheckpoint: checkpoint,
|
|
137
|
+
});
|
|
96
138
|
const latest = await loadPrincipalPolicyCheckpoint(input.execSql, input.expectedHead.principalType, input.expectedHead.principalId);
|
|
97
139
|
if (latest?.version !== checkpoint?.version ||
|
|
98
140
|
latest?.stateHash !== checkpoint?.stateHash)
|
|
99
|
-
throw new
|
|
141
|
+
throw new PrincipalHistoryRecoveryRaceError("Local principal checkpoint changed during history recovery");
|
|
142
|
+
assertCurrent(input);
|
|
143
|
+
if (!input.offline)
|
|
144
|
+
await publishReusablePrincipalHistoryPrefix(input, stage.scopeId, stage.verifier, stage.current);
|
|
100
145
|
assertCurrent(input);
|
|
101
146
|
return { current: stage.current, policy: current.value };
|
|
102
147
|
}
|
|
103
|
-
async function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
principalId: input.expectedHead.principalId,
|
|
111
|
-
localCheckpoint: checkpoint,
|
|
112
|
-
retainedReferences: input.retainedReferences ?? [],
|
|
113
|
-
};
|
|
114
|
-
const stage = await restorePrincipalHistoryRecoveryStage(input, historyInput);
|
|
148
|
+
async function readRecoveryPages(input, stage) {
|
|
149
|
+
if (input.offline) {
|
|
150
|
+
if (!stage.complete || !stage.current)
|
|
151
|
+
throw new KeyingVerificationError("missing_dependency", "Completed principal history is unavailable offline");
|
|
152
|
+
assertExpectedHead(stage.current, input.expectedHead);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
115
155
|
let receivedFinalPage = false;
|
|
116
156
|
for await (const result of input.apiClient.getPrincipalPolicyPages(input.expectedHead.principalType, input.expectedHead.principalId, {
|
|
117
157
|
signal: input.signal,
|
|
@@ -123,13 +163,13 @@ async function recover(input) {
|
|
|
123
163
|
afterVersion: stage.saved.afterVersion,
|
|
124
164
|
},
|
|
125
165
|
}
|
|
126
|
-
: {}),
|
|
166
|
+
: { afterVersion: stage.initialAfterVersion }),
|
|
127
167
|
})) {
|
|
128
168
|
if (!result.ok) {
|
|
129
169
|
// A rejected transport pin cannot justify skipping history on retry.
|
|
130
170
|
// Discard only this operation's progress, then let a new call replay it.
|
|
131
171
|
if (result.kind === "shape" && stage.saved)
|
|
132
|
-
await discardPrincipalHistoryStage(input.execSql, stage.saved, () =>
|
|
172
|
+
await discardPrincipalHistoryStage(input.execSql, stage.saved, () => recoveryIsCurrent(input));
|
|
133
173
|
throw new PrincipalPolicyHistoryReadError(result);
|
|
134
174
|
}
|
|
135
175
|
await acceptPage(input, stage, result.data);
|
|
@@ -137,33 +177,65 @@ async function recover(input) {
|
|
|
137
177
|
}
|
|
138
178
|
if (!receivedFinalPage)
|
|
139
179
|
throw new KeyingVerificationError("missing_dependency", "Principal history transport did not complete");
|
|
140
|
-
|
|
180
|
+
}
|
|
181
|
+
async function recover(input, allowEvidenceRebuild = true) {
|
|
182
|
+
assertCurrent(input);
|
|
183
|
+
const checkpoint = await loadPrincipalPolicyCheckpoint(input.execSql, input.expectedHead.principalType, input.expectedHead.principalId);
|
|
184
|
+
if (checkpoint && input.expectedHead.version < checkpoint.version)
|
|
185
|
+
throw new KeyingVerificationError(input.offline ? "missing_dependency" : "rollback", "Requested principal history predates the local checkpoint");
|
|
186
|
+
// Validate the requested pin/selection before creating any disposable caches.
|
|
187
|
+
createPrincipalPolicyHistoryVerifier({
|
|
188
|
+
principalType: input.expectedHead.principalType,
|
|
189
|
+
principalId: input.expectedHead.principalId,
|
|
190
|
+
localCheckpoint: checkpoint,
|
|
191
|
+
retainedReferences: input.retainedReferences ?? [],
|
|
192
|
+
});
|
|
193
|
+
const historyInput = {
|
|
194
|
+
principalType: input.expectedHead.principalType,
|
|
195
|
+
principalId: input.expectedHead.principalId,
|
|
196
|
+
};
|
|
197
|
+
const stage = await restorePrincipalHistoryRecoveryStage(input, historyInput, allowEvidenceRebuild);
|
|
198
|
+
try {
|
|
199
|
+
await readRecoveryPages(input, stage);
|
|
200
|
+
return await finishRecovery(input, stage, checkpoint);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
const lostEvidence = error instanceof PrincipalHistoryEvidenceUnavailableError;
|
|
204
|
+
const disconnected = error instanceof PrincipalHistoryPrefixDisconnectedError;
|
|
205
|
+
if (!lostEvidence && !disconnected)
|
|
206
|
+
throw error;
|
|
207
|
+
if (stage.saved)
|
|
208
|
+
await discardPrincipalHistoryStage(input.execSql, stage.saved, () => recoveryIsCurrent(input));
|
|
209
|
+
if (!input.offline &&
|
|
210
|
+
allowEvidenceRebuild &&
|
|
211
|
+
(lostEvidence || stage.fromCache))
|
|
212
|
+
return recover(input, false);
|
|
213
|
+
throw lostEvidence || disconnected ? error.verificationError : error;
|
|
214
|
+
}
|
|
141
215
|
}
|
|
142
216
|
/**
|
|
143
217
|
* Recover one exact head with bounded retained history and durable checked progress.
|
|
144
218
|
* The result is a sparse current-policy capability; this does not advance app checkpoints.
|
|
219
|
+
* Retain organization version 1 when the result will establish founder binding.
|
|
145
220
|
*/
|
|
146
221
|
export async function recoverPrincipalPolicyHistory(options) {
|
|
222
|
+
if (options.offline !== undefined && typeof options.offline !== "boolean")
|
|
223
|
+
throw new KeyingVerificationError("invalid_shape", "Invalid principal history transport mode");
|
|
147
224
|
if (!options.organizationId ||
|
|
148
225
|
(options.expectedHead.principalType === "organization" &&
|
|
149
226
|
options.expectedHead.principalId !== options.organizationId))
|
|
150
227
|
throw new KeyingVerificationError("object_mismatch", "Principal history organization scope is invalid");
|
|
151
228
|
if (options.retainedReferences?.some((reference) => reference.version > options.expectedHead.version))
|
|
152
229
|
throw new KeyingVerificationError("missing_dependency", "Requested history reference is beyond the recovery head");
|
|
153
|
-
if (!(options.protection.localKey instanceof Uint8Array) ||
|
|
154
|
-
options.protection.localKey.byteLength !== 32 ||
|
|
155
|
-
!options.protection.context)
|
|
156
|
-
throw new KeyingVerificationError("invalid_shape", "Principal history recovery requires a private 32-byte key and trust context");
|
|
157
230
|
const input = {
|
|
158
231
|
...options,
|
|
232
|
+
historyVerification: options.historyVerification ?? "standard",
|
|
159
233
|
expectedHead: structuredClone(options.expectedHead),
|
|
160
234
|
retainedReferences: structuredClone(options.retainedReferences ?? []),
|
|
161
|
-
protection:
|
|
162
|
-
context: options.protection.context,
|
|
163
|
-
localKey: new Uint8Array(options.protection.localKey),
|
|
164
|
-
},
|
|
235
|
+
protection: ownPrincipalHistoryProtection(options.protection),
|
|
165
236
|
};
|
|
166
237
|
try {
|
|
238
|
+
input.protection.context = principalHistoryVerificationContext(input);
|
|
167
239
|
return await recover(input);
|
|
168
240
|
}
|
|
169
241
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recoverPrincipalPolicyHistory.js","sourceRoot":"","sources":["recoverPrincipalPolicyHistory.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PrincipalPolicyPageCurrent } from \"@tearleads/api-client\";\nimport {\n KeyingVerificationError,\n type PrincipalPolicyCheckpoint,\n type PrincipalPolicyHistoryInput,\n verifyPrincipalPolicyCurrent,\n} from \"@tearleads/crypto\";\nimport type { PrincipalPolicyPageResponse } from \"@tearleads/validators/response\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport {\n discardPrincipalHistoryStage,\n savePrincipalHistoryStage,\n} from \"../../data/persistence/principalHistoryStagePersistence\";\nimport { principalHistoryStageProtection } from \"../../data/principals/principalHistoryStageProtection\";\nimport { collectPrincipalPolicySignerPublicKeys } from \"./policyVerification\";\nimport {\n type PrincipalHistoryRecoveryStage,\n restorePrincipalHistoryRecoveryStage,\n} from \"./principalHistoryRecoveryStage\";\nimport {\n PrincipalPolicyHistoryReadError,\n type RecoveredPrincipalPolicyHistory,\n type RecoverPrincipalPolicyHistoryOptions,\n} from \"./principalHistoryRecoveryTypes\";\n\nfunction currentArtifacts(\n page: PrincipalPolicyPageResponse,\n): PrincipalPolicyPageCurrent {\n return {\n currentState: page.currentState,\n currentPayload: page.currentPayload,\n currentProjection: page.currentProjection,\n currentGrants: page.currentGrants,\n currentMemberEnvelopes: page.currentMemberEnvelopes,\n };\n}\n\nfunction assertCurrent(input: RecoverPrincipalPolicyHistoryOptions): void {\n assertProjectionVerificationCurrent(\n () => !input.signal?.aborted && input.stillCurrent(),\n );\n}\n\nasync function acceptPage(\n input: RecoverPrincipalPolicyHistoryOptions,\n stage: PrincipalHistoryRecoveryStage,\n response: PrincipalPolicyPageResponse,\n): Promise<void> {\n const page = structuredClone(response);\n assertCurrent(input);\n for (const field of [\n \"principalType\",\n \"principalId\",\n \"version\",\n \"stateHash\",\n \"keyEpoch\",\n \"keyFingerprint\",\n ] as const) {\n if (page.currentState[field] !== input.expectedHead[field])\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Principal history page does not match its requested head\",\n );\n }\n if (stage.saved?.complete) {\n if (\n page.previousStates.length !== 0 ||\n page.historyPage.nextAfterVersion !== null\n )\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"Completed history returned additional entries\",\n );\n return;\n }\n const complete = page.historyPage.nextAfterVersion === null;\n const current = currentArtifacts(page);\n const entries = [...page.previousStates];\n if (complete)\n entries.push({\n state: current.currentState,\n projection: current.currentProjection,\n grants: current.currentGrants,\n });\n const keys = await collectPrincipalPolicySignerPublicKeys({\n bundle: { ...current, previousStates: page.previousStates },\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in keys)\n throw new KeyingVerificationError(\n keys.error === \"not-found\" ? \"missing_dependency\" : \"signer_mismatch\",\n \"Principal history signer identity could not be authenticated\",\n );\n const externalAuthority = await input.loadExternalAuthority?.(entries);\n const appended = await stage.verifier.append({\n entries,\n signerPublicKeys: keys.signerPublicKeys,\n ...(externalAuthority ? { externalAuthority } : {}),\n });\n if (!appended.ok) throw appended.error;\n const next = {\n id: stage.id,\n organizationId: input.organizationId,\n currentJson: JSON.stringify(current),\n afterVersion: appended.value.throughVersion - (complete ? 1 : 0),\n complete,\n };\n const protectedProgress = await stage.verifier.exportProgress(\n await principalHistoryStageProtection(\n input.protection,\n input.expectedHead,\n next,\n ),\n );\n if (!protectedProgress.ok) throw protectedProgress.error;\n const saved = { ...next, progress: protectedProgress.value };\n await savePrincipalHistoryStage({\n execSql: input.execSql,\n stage: saved,\n previousProgress: stage.saved?.progress ?? null,\n stillCurrent: () => !input.signal?.aborted && input.stillCurrent(),\n });\n stage.current = current;\n stage.saved = saved;\n}\n\nasync function finishRecovery(\n input: RecoverPrincipalPolicyHistoryOptions,\n stage: PrincipalHistoryRecoveryStage,\n checkpoint: PrincipalPolicyCheckpoint | null,\n): Promise<RecoveredPrincipalPolicyHistory> {\n if (!stage.current || !stage.saved?.complete)\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Principal history ended before its pinned head\",\n );\n const history = stage.verifier.finish(input.expectedHead);\n if (!history.ok) throw history.error;\n const current = await verifyPrincipalPolicyCurrent({\n current: stage.current,\n history: history.value,\n });\n if (!current.ok) throw current.error;\n const latest = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n input.expectedHead.principalType,\n input.expectedHead.principalId,\n );\n if (\n latest?.version !== checkpoint?.version ||\n latest?.stateHash !== checkpoint?.stateHash\n )\n throw new KeyingVerificationError(\n \"stale_predecessor\",\n \"Local principal checkpoint changed during history recovery\",\n );\n assertCurrent(input);\n return { current: stage.current, policy: current.value };\n}\n\nasync function recover(\n input: RecoverPrincipalPolicyHistoryOptions,\n): Promise<RecoveredPrincipalPolicyHistory> {\n assertCurrent(input);\n const checkpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n input.expectedHead.principalType,\n input.expectedHead.principalId,\n );\n if (checkpoint && input.expectedHead.version < checkpoint.version)\n throw new KeyingVerificationError(\n \"rollback\",\n \"Requested principal history predates the local checkpoint\",\n );\n const historyInput: PrincipalPolicyHistoryInput = {\n principalType: input.expectedHead.principalType,\n principalId: input.expectedHead.principalId,\n localCheckpoint: checkpoint,\n retainedReferences: input.retainedReferences ?? [],\n };\n const stage = await restorePrincipalHistoryRecoveryStage(input, historyInput);\n let receivedFinalPage = false;\n for await (const result of input.apiClient.getPrincipalPolicyPages(\n input.expectedHead.principalType,\n input.expectedHead.principalId,\n {\n signal: input.signal,\n stateHash: input.expectedHead.stateHash,\n ...(stage.saved && stage.current\n ? {\n resume: {\n current: stage.current,\n afterVersion: stage.saved.afterVersion,\n },\n }\n : {}),\n },\n )) {\n if (!result.ok) {\n // A rejected transport pin cannot justify skipping history on retry.\n // Discard only this operation's progress, then let a new call replay it.\n if (result.kind === \"shape\" && stage.saved)\n await discardPrincipalHistoryStage(\n input.execSql,\n stage.saved,\n () => !input.signal?.aborted && input.stillCurrent(),\n );\n throw new PrincipalPolicyHistoryReadError(result);\n }\n await acceptPage(input, stage, result.data);\n receivedFinalPage = result.data.historyPage.nextAfterVersion === null;\n }\n if (!receivedFinalPage)\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Principal history transport did not complete\",\n );\n return finishRecovery(input, stage, checkpoint);\n}\n\n/**\n * Recover one exact head with bounded retained history and durable checked progress.\n * The result is a sparse current-policy capability; this does not advance app checkpoints.\n */\nexport async function recoverPrincipalPolicyHistory(\n options: RecoverPrincipalPolicyHistoryOptions,\n): Promise<RecoveredPrincipalPolicyHistory> {\n if (\n !options.organizationId ||\n (options.expectedHead.principalType === \"organization\" &&\n options.expectedHead.principalId !== options.organizationId)\n )\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Principal history organization scope is invalid\",\n );\n if (\n options.retainedReferences?.some(\n (reference) => reference.version > options.expectedHead.version,\n )\n )\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Requested history reference is beyond the recovery head\",\n );\n if (\n !(options.protection.localKey instanceof Uint8Array) ||\n options.protection.localKey.byteLength !== 32 ||\n !options.protection.context\n )\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"Principal history recovery requires a private 32-byte key and trust context\",\n );\n const input = {\n ...options,\n expectedHead: structuredClone(options.expectedHead),\n retainedReferences: structuredClone(options.retainedReferences ?? []),\n protection: {\n context: options.protection.context,\n localKey: new Uint8Array(options.protection.localKey),\n },\n };\n try {\n return await recover(input);\n } finally {\n input.protection.localKey.fill(0);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recoverPrincipalPolicyHistory.js","sourceRoot":"","sources":["recoverPrincipalPolicyHistory.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PrincipalPolicyPageCurrent } from \"@tearleads/api-client\";\nimport {\n createPrincipalPolicyHistoryVerifier,\n KeyingVerificationError,\n type PrincipalPolicyCheckpoint,\n type PrincipalPolicyHistoryInput,\n verifyPrincipalPolicyCheckpoint,\n verifyPrincipalPolicyCurrent,\n} from \"@tearleads/crypto\";\nimport type { PrincipalPolicyPageResponse } from \"@tearleads/validators/response\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport { preparePrincipalHistoryEvidencePage } from \"../../data/persistence/principalHistoryEvidencePersistence\";\nimport {\n discardPrincipalHistoryStage,\n savePrincipalHistoryStage,\n} from \"../../data/persistence/principalHistoryStagePersistence\";\nimport { ownPrincipalHistoryProtection } from \"../../data/principals/principalHistoryProtection\";\nimport { principalHistoryStageProtection } from \"../../data/principals/principalHistoryStageProtection\";\nimport { collectPrincipalPolicySignerPublicKeys } from \"./policyVerification\";\nimport { publishReusablePrincipalHistoryPrefix } from \"./principalHistoryRecoveryPrefix\";\nimport {\n PrincipalHistoryEvidenceUnavailableError,\n selectRecoveredPrincipalHistory,\n} from \"./principalHistoryRecoveryReferences\";\nimport {\n type PrincipalHistoryRecoveryStage,\n restorePrincipalHistoryRecoveryStage,\n} from \"./principalHistoryRecoveryStage\";\nimport {\n PrincipalHistoryRecoveryRaceError,\n PrincipalPolicyHistoryReadError,\n type RecoveredPrincipalPolicyHistory,\n type RecoverPrincipalPolicyHistoryOptions,\n} from \"./principalHistoryRecoveryTypes\";\nimport {\n assertPrincipalHistoryVerificationMode,\n principalHistoryVerificationContext,\n} from \"./principalHistoryRecoveryVerification\";\n\nclass PrincipalHistoryPrefixDisconnectedError extends Error {\n constructor(readonly verificationError: KeyingVerificationError) {\n super(verificationError.message);\n this.name = \"PrincipalHistoryPrefixDisconnectedError\";\n }\n}\n\nfunction currentArtifacts(\n page: PrincipalPolicyPageResponse,\n): PrincipalPolicyPageCurrent {\n return {\n currentState: page.currentState,\n currentPayload: page.currentPayload,\n currentProjection: page.currentProjection,\n currentGrants: page.currentGrants,\n currentMemberEnvelopes: page.currentMemberEnvelopes,\n };\n}\n\nfunction recoveryIsCurrent(\n input: RecoverPrincipalPolicyHistoryOptions,\n): boolean {\n return !input.signal?.aborted && input.stillCurrent();\n}\n\nfunction assertCurrent(input: RecoverPrincipalPolicyHistoryOptions): void {\n assertProjectionVerificationCurrent(() => recoveryIsCurrent(input));\n}\n\nfunction assertExpectedHead(\n current: PrincipalPolicyPageCurrent,\n expectedHead: RecoverPrincipalPolicyHistoryOptions[\"expectedHead\"],\n): void {\n for (const field of [\n \"principalType\",\n \"principalId\",\n \"version\",\n \"stateHash\",\n \"keyEpoch\",\n \"keyFingerprint\",\n ] as const) {\n if (current.currentState[field] !== expectedHead[field])\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Principal history page does not match its requested head\",\n );\n }\n}\n\nasync function acceptPage(\n input: RecoverPrincipalPolicyHistoryOptions,\n stage: PrincipalHistoryRecoveryStage,\n response: PrincipalPolicyPageResponse,\n): Promise<void> {\n const page = structuredClone(response);\n assertCurrent(input);\n assertExpectedHead(page, input.expectedHead);\n if (stage.complete) {\n if (\n page.previousStates.length !== 0 ||\n page.historyPage.nextAfterVersion !== null\n )\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"Completed history returned additional entries\",\n );\n stage.current = currentArtifacts(page);\n return;\n }\n const complete = page.historyPage.nextAfterVersion === null;\n const current = currentArtifacts(page);\n const entries = [...page.previousStates];\n if (complete)\n entries.push({\n state: current.currentState,\n projection: current.currentProjection,\n grants: current.currentGrants,\n });\n assertPrincipalHistoryVerificationMode(input, entries);\n const keys = await collectPrincipalPolicySignerPublicKeys({\n bundle: { ...current, previousStates: page.previousStates },\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in keys)\n throw new KeyingVerificationError(\n keys.error === \"not-found\" ? \"missing_dependency\" : \"signer_mismatch\",\n \"Principal history signer identity could not be authenticated\",\n );\n const externalAuthority = await input.loadExternalAuthority?.(entries);\n const appended = await stage.verifier.append({\n entries,\n signerPublicKeys: keys.signerPublicKeys,\n ...(externalAuthority ? { externalAuthority } : {}),\n });\n if (!appended.ok) {\n if (appended.error.code === \"stale_predecessor\")\n throw new PrincipalHistoryPrefixDisconnectedError(appended.error);\n throw appended.error;\n }\n const next = {\n id: stage.id,\n organizationId: input.organizationId,\n currentJson: JSON.stringify(current),\n afterVersion: appended.value.throughVersion - (complete ? 1 : 0),\n complete,\n };\n const protectedProgress = await stage.verifier.exportProgress(\n await principalHistoryStageProtection(\n input.protection,\n input.expectedHead,\n next,\n ),\n );\n if (!protectedProgress.ok) throw protectedProgress.error;\n const saved = { ...next, progress: protectedProgress.value };\n await savePrincipalHistoryStage({\n execSql: input.execSql,\n stage: saved,\n evidence: await preparePrincipalHistoryEvidencePage({\n scopeId: stage.scopeId,\n organizationId: input.organizationId,\n entries,\n nodes: appended.value.indexNodes,\n }),\n previousProgress: stage.saved?.progress ?? null,\n stillCurrent: () => recoveryIsCurrent(input),\n });\n stage.current = current;\n stage.saved = saved;\n stage.complete = complete;\n}\n\nasync function finishRecovery(\n input: RecoverPrincipalPolicyHistoryOptions,\n stage: PrincipalHistoryRecoveryStage,\n checkpoint: PrincipalPolicyCheckpoint | null,\n): Promise<RecoveredPrincipalPolicyHistory> {\n if (!stage.current || !stage.complete)\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Principal history ended before its pinned head\",\n );\n const history = stage.verifier.finish(input.expectedHead);\n if (!history.ok) throw history.error;\n const selected = await selectRecoveredPrincipalHistory({\n options: input,\n scopeId: stage.scopeId,\n history: history.value,\n checkpoint,\n });\n const current = await verifyPrincipalPolicyCurrent({\n current: stage.current,\n history: selected,\n });\n if (!current.ok) throw current.error;\n verifyPrincipalPolicyCheckpoint({\n chain: current.value.retainedHistory,\n currentState: current.value.state,\n localCheckpoint: checkpoint,\n });\n const latest = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n input.expectedHead.principalType,\n input.expectedHead.principalId,\n );\n if (\n latest?.version !== checkpoint?.version ||\n latest?.stateHash !== checkpoint?.stateHash\n )\n throw new PrincipalHistoryRecoveryRaceError(\n \"Local principal checkpoint changed during history recovery\",\n );\n assertCurrent(input);\n if (!input.offline)\n await publishReusablePrincipalHistoryPrefix(\n input,\n stage.scopeId,\n stage.verifier,\n stage.current,\n );\n assertCurrent(input);\n return { current: stage.current, policy: current.value };\n}\n\nasync function readRecoveryPages(\n input: RecoverPrincipalPolicyHistoryOptions,\n stage: PrincipalHistoryRecoveryStage,\n): Promise<void> {\n if (input.offline) {\n if (!stage.complete || !stage.current)\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Completed principal history is unavailable offline\",\n );\n assertExpectedHead(stage.current, input.expectedHead);\n return;\n }\n let receivedFinalPage = false;\n for await (const result of input.apiClient.getPrincipalPolicyPages(\n input.expectedHead.principalType,\n input.expectedHead.principalId,\n {\n signal: input.signal,\n stateHash: input.expectedHead.stateHash,\n ...(stage.saved && stage.current\n ? {\n resume: {\n current: stage.current,\n afterVersion: stage.saved.afterVersion,\n },\n }\n : { afterVersion: stage.initialAfterVersion }),\n },\n )) {\n if (!result.ok) {\n // A rejected transport pin cannot justify skipping history on retry.\n // Discard only this operation's progress, then let a new call replay it.\n if (result.kind === \"shape\" && stage.saved)\n await discardPrincipalHistoryStage(input.execSql, stage.saved, () =>\n recoveryIsCurrent(input),\n );\n throw new PrincipalPolicyHistoryReadError(result);\n }\n await acceptPage(input, stage, result.data);\n receivedFinalPage = result.data.historyPage.nextAfterVersion === null;\n }\n if (!receivedFinalPage)\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Principal history transport did not complete\",\n );\n}\n\nasync function recover(\n input: RecoverPrincipalPolicyHistoryOptions,\n allowEvidenceRebuild = true,\n): Promise<RecoveredPrincipalPolicyHistory> {\n assertCurrent(input);\n const checkpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n input.expectedHead.principalType,\n input.expectedHead.principalId,\n );\n if (checkpoint && input.expectedHead.version < checkpoint.version)\n throw new KeyingVerificationError(\n input.offline ? \"missing_dependency\" : \"rollback\",\n \"Requested principal history predates the local checkpoint\",\n );\n // Validate the requested pin/selection before creating any disposable caches.\n createPrincipalPolicyHistoryVerifier({\n principalType: input.expectedHead.principalType,\n principalId: input.expectedHead.principalId,\n localCheckpoint: checkpoint,\n retainedReferences: input.retainedReferences ?? [],\n });\n const historyInput: PrincipalPolicyHistoryInput = {\n principalType: input.expectedHead.principalType,\n principalId: input.expectedHead.principalId,\n };\n const stage = await restorePrincipalHistoryRecoveryStage(\n input,\n historyInput,\n allowEvidenceRebuild,\n );\n try {\n await readRecoveryPages(input, stage);\n return await finishRecovery(input, stage, checkpoint);\n } catch (error) {\n const lostEvidence =\n error instanceof PrincipalHistoryEvidenceUnavailableError;\n const disconnected =\n error instanceof PrincipalHistoryPrefixDisconnectedError;\n if (!lostEvidence && !disconnected) throw error;\n if (stage.saved)\n await discardPrincipalHistoryStage(input.execSql, stage.saved, () =>\n recoveryIsCurrent(input),\n );\n if (\n !input.offline &&\n allowEvidenceRebuild &&\n (lostEvidence || stage.fromCache)\n )\n return recover(input, false);\n throw lostEvidence || disconnected ? error.verificationError : error;\n }\n}\n\n/**\n * Recover one exact head with bounded retained history and durable checked progress.\n * The result is a sparse current-policy capability; this does not advance app checkpoints.\n * Retain organization version 1 when the result will establish founder binding.\n */\nexport async function recoverPrincipalPolicyHistory(\n options: RecoverPrincipalPolicyHistoryOptions,\n): Promise<RecoveredPrincipalPolicyHistory> {\n if (options.offline !== undefined && typeof options.offline !== \"boolean\")\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"Invalid principal history transport mode\",\n );\n if (\n !options.organizationId ||\n (options.expectedHead.principalType === \"organization\" &&\n options.expectedHead.principalId !== options.organizationId)\n )\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Principal history organization scope is invalid\",\n );\n if (\n options.retainedReferences?.some(\n (reference) => reference.version > options.expectedHead.version,\n )\n )\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Requested history reference is beyond the recovery head\",\n );\n const input = {\n ...options,\n historyVerification: options.historyVerification ?? \"standard\",\n expectedHead: structuredClone(options.expectedHead),\n retainedReferences: structuredClone(options.retainedReferences ?? []),\n protection: ownPrincipalHistoryProtection(options.protection),\n };\n try {\n input.protection.context = principalHistoryVerificationContext(input);\n return await recover(input);\n } finally {\n input.protection.localKey.fill(0);\n }\n}\n"]}
|