@tearleads/client-sdk 0.1.104 → 0.1.105

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.
@@ -5,6 +5,7 @@ import { ProjectionDependencyUnavailableError } from "../dependencyUnavailable.j
5
5
  import { projectionLifetimeGuard } from "../projectionLifetimes.js";
6
6
  import { assertProjectionVerificationCurrent } from "../types.js";
7
7
  import { enforceAccessManifestCheckpoints, validateAccessManifestCheckpoints, } from "./accessManifestCheckpointEnforcement.js";
8
+ import { matchesVerifiedBundleBytes } from "./verifiedBundleBytes.js";
8
9
  export function createProjectionCheckpointContext(input) {
9
10
  if (typeof input.execSql !== "function") {
10
11
  throw new KeyingVerificationError("missing_dependency", "Projection verification requires durable keying checkpoint storage");
@@ -34,7 +35,9 @@ export function observeAccessManifestCheckpoints(context, input) {
34
35
  export function observeVerifiedProjectionBundles(context, bundlesByHash, verifiedByHash) {
35
36
  for (const [manifestHash, bundle] of bundlesByHash) {
36
37
  const verified = verifiedByHash.get(manifestHash);
37
- if (verified)
38
+ // A cache hit verifies the hash, not these bytes: store only bytes that
39
+ // a fresh verification accepted, so a citation never replays others.
40
+ if (verified && matchesVerifiedBundleBytes(verified, bundle))
38
41
  context.verifiedBundles.push({ bundle, checkpoint: verified.checkpoint });
39
42
  }
40
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"checkpointContext.js","sourceRoot":"","sources":["checkpointContext.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type AccessManifestCheckpoint,\n KeyingVerificationError,\n type VerifiedAccessManifestCheckpointEvidence,\n type VerifiedContainerAccessManifest,\n type VerifiedPrincipalPolicySelection,\n} from \"@tearleads/crypto\";\nimport type { AccessManifestBundleWireResponse } from \"@tearleads/validators/response\";\nimport { rememberVerifiedContainerHeads } from \"../../containers/shared/heldContainerHeads\";\nimport type { DocumentPurgeCheckpoint } from \"../../persistence/keyingCheckpoints/documentPurgeCheckpointPersistence\";\nimport { PrincipalAuthorizationCheckpointUnavailableError } from \"../../persistence/keyingCheckpoints/principalAuthorizationCheckpoints\";\nimport type { VerifiedManifestBundle } from \"../../persistence/verifiedManifestBundlePersistence\";\nimport type { PrincipalPolicyCheckpointEvidence } from \"../../principals/principalPolicyEvidence\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport { ProjectionDependencyUnavailableError } from \"../dependencyUnavailable\";\nimport { projectionLifetimeGuard } from \"../projectionLifetimes\";\nimport { assertProjectionVerificationCurrent } from \"../types\";\nimport {\n enforceAccessManifestCheckpoints,\n validateAccessManifestCheckpoints,\n} from \"./accessManifestCheckpointEnforcement\";\n\nexport interface ProjectionCheckpointContext {\n readonly heldContainerHeads: VerifiedContainerAccessManifest[];\n readonly execSql: ExecSql;\n // Local checkpoints read during this verification, one read per object.\n readonly localCheckpoints: Map<string, AccessManifestCheckpoint | null>;\n organizationId?: string | undefined;\n readonly policies: PrincipalPolicyCheckpointEvidence[];\n readonly authorizationPolicies: VerifiedPrincipalPolicySelection[];\n // Served bundles whose manifests verified, stored at finalization.\n readonly verifiedBundles: VerifiedManifestBundle[];\n readonly verifiedHeads: VerifiedAccessManifestCheckpointEvidence[];\n readonly verifiedManifests: VerifiedAccessManifestCheckpointEvidence[];\n}\n\nexport function createProjectionCheckpointContext(input: {\n readonly execSql: ExecSql;\n readonly organizationId?: string | undefined;\n}): ProjectionCheckpointContext {\n if (typeof input.execSql !== \"function\") {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Projection verification requires durable keying checkpoint storage\",\n );\n }\n\n return {\n heldContainerHeads: [],\n execSql: input.execSql,\n localCheckpoints: new Map(),\n organizationId: input.organizationId,\n policies: [],\n authorizationPolicies: [],\n verifiedBundles: [],\n verifiedHeads: [],\n verifiedManifests: [],\n };\n}\n\nexport function observeAccessManifestCheckpoints(\n context: ProjectionCheckpointContext,\n input: {\n readonly verifiedHeads: readonly VerifiedAccessManifestCheckpointEvidence[];\n readonly verifiedManifests: readonly VerifiedAccessManifestCheckpointEvidence[];\n },\n): void {\n context.verifiedHeads.push(...input.verifiedHeads);\n context.verifiedManifests.push(...input.verifiedManifests);\n}\n\n/**\n * Records each served bundle whose manifest this verification produced, so\n * the transaction finalizing the checkpoints also stores it for later\n * citation. A bundle the projection carried but verification never reached\n * is skipped.\n */\nexport function observeVerifiedProjectionBundles(\n context: ProjectionCheckpointContext,\n bundlesByHash: ReadonlyMap<string, AccessManifestBundleWireResponse>,\n verifiedByHash: ReadonlyMap<\n string,\n { readonly checkpoint: AccessManifestCheckpoint }\n >,\n): void {\n for (const [manifestHash, bundle] of bundlesByHash) {\n const verified = verifiedByHash.get(manifestHash);\n if (verified)\n context.verifiedBundles.push({ bundle, checkpoint: verified.checkpoint });\n }\n}\n\nexport function observePrincipalPolicy(\n context: ProjectionCheckpointContext,\n policy: PrincipalPolicyCheckpointEvidence,\n organizationId?: string | undefined,\n): void {\n if (\n organizationId !== undefined &&\n context.organizationId !== undefined &&\n context.organizationId !== organizationId\n ) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"projection checkpoint batch crosses organization ownership\",\n );\n }\n context.organizationId ??= organizationId;\n context.policies.push(policy);\n}\n\nexport async function commitProjectionCheckpoints(\n context: ProjectionCheckpointContext,\n input?: {\n readonly documentPurgeCheckpoint?: DocumentPurgeCheckpoint | undefined;\n readonly execSql?: ExecSql | undefined;\n readonly stillCurrent?: (() => boolean) | undefined;\n },\n): Promise<void> {\n input = {\n ...input,\n stillCurrent: projectionLifetimeGuard(context, input?.stillCurrent),\n };\n assertProjectionVerificationCurrent(input?.stillCurrent);\n await enforceAccessManifestCheckpoints({\n documentPurgeCheckpoint: input?.documentPurgeCheckpoint,\n execSql: input?.execSql ?? context.execSql,\n organizationId: context.organizationId,\n policies: context.policies,\n authorizationPolicies: context.authorizationPolicies,\n stillCurrent: input?.stillCurrent,\n verifiedBundles: context.verifiedBundles,\n verifiedHeads: context.verifiedHeads,\n verifiedManifests: context.verifiedManifests,\n }).catch((error: unknown) => {\n if (error instanceof PrincipalAuthorizationCheckpointUnavailableError)\n throw new ProjectionDependencyUnavailableError(error.message);\n throw error;\n });\n assertProjectionVerificationCurrent(input?.stillCurrent);\n}\n\n/** Validates against the latest durable pins without persisting new heads. */\nasync function validateProjectionCheckpoints(\n context: ProjectionCheckpointContext,\n input?: {\n readonly execSql?: ExecSql | undefined;\n readonly stillCurrent?: (() => boolean) | undefined;\n },\n): Promise<void> {\n input = {\n ...input,\n stillCurrent: projectionLifetimeGuard(context, input?.stillCurrent),\n };\n assertProjectionVerificationCurrent(input?.stillCurrent);\n await validateAccessManifestCheckpoints({\n execSql: input?.execSql ?? context.execSql,\n policies: context.policies,\n authorizationPolicies: context.authorizationPolicies,\n stillCurrent: input?.stillCurrent,\n verifiedBundles: context.verifiedBundles,\n verifiedHeads: context.verifiedHeads,\n verifiedManifests: context.verifiedManifests,\n }).catch((error: unknown) => {\n if (error instanceof PrincipalAuthorizationCheckpointUnavailableError)\n throw new ProjectionDependencyUnavailableError(error.message);\n throw error;\n });\n assertProjectionVerificationCurrent(input?.stillCurrent);\n}\n\nexport async function finalizeProjectionCheckpoints(\n context: ProjectionCheckpointContext,\n input: {\n readonly execSql?: ExecSql | undefined;\n readonly persistVerificationCheckpoints?: boolean | undefined;\n readonly stillCurrent?: (() => boolean) | undefined;\n },\n): Promise<void> {\n if (input.persistVerificationCheckpoints === false) {\n await validateProjectionCheckpoints(context, input);\n return;\n }\n await commitProjectionCheckpoints(context, input);\n if (input.stillCurrent?.() === false) return;\n const organizationId =\n context.organizationId ??\n context.heldContainerHeads[0]?.state.organizationId;\n if (organizationId === undefined) return;\n try {\n rememberVerifiedContainerHeads({\n organizationId,\n execSql: input.execSql ?? context.execSql,\n heads: context.heldContainerHeads,\n policies: context.policies,\n });\n } catch {\n // Checkpoints are already committed. Optional re-citation evidence must\n // never change successful verification into a caller-visible refusal.\n }\n}\n"]}
1
+ {"version":3,"file":"checkpointContext.js","sourceRoot":"","sources":["checkpointContext.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type AccessManifestCheckpoint,\n KeyingVerificationError,\n type VerifiedAccessManifestCheckpointEvidence,\n type VerifiedContainerAccessManifest,\n type VerifiedPrincipalPolicySelection,\n} from \"@tearleads/crypto\";\nimport type { AccessManifestBundleWireResponse } from \"@tearleads/validators/response\";\nimport { rememberVerifiedContainerHeads } from \"../../containers/shared/heldContainerHeads\";\nimport type { DocumentPurgeCheckpoint } from \"../../persistence/keyingCheckpoints/documentPurgeCheckpointPersistence\";\nimport { PrincipalAuthorizationCheckpointUnavailableError } from \"../../persistence/keyingCheckpoints/principalAuthorizationCheckpoints\";\nimport type { VerifiedManifestBundle } from \"../../persistence/verifiedManifestBundlePersistence\";\nimport type { PrincipalPolicyCheckpointEvidence } from \"../../principals/principalPolicyEvidence\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport { ProjectionDependencyUnavailableError } from \"../dependencyUnavailable\";\nimport { projectionLifetimeGuard } from \"../projectionLifetimes\";\nimport { assertProjectionVerificationCurrent } from \"../types\";\nimport {\n enforceAccessManifestCheckpoints,\n validateAccessManifestCheckpoints,\n} from \"./accessManifestCheckpointEnforcement\";\nimport { matchesVerifiedBundleBytes } from \"./verifiedBundleBytes\";\n\nexport interface ProjectionCheckpointContext {\n readonly heldContainerHeads: VerifiedContainerAccessManifest[];\n readonly execSql: ExecSql;\n // Local checkpoints read during this verification, one read per object.\n readonly localCheckpoints: Map<string, AccessManifestCheckpoint | null>;\n organizationId?: string | undefined;\n readonly policies: PrincipalPolicyCheckpointEvidence[];\n readonly authorizationPolicies: VerifiedPrincipalPolicySelection[];\n // Served bundles whose manifests verified, stored at finalization.\n readonly verifiedBundles: VerifiedManifestBundle[];\n readonly verifiedHeads: VerifiedAccessManifestCheckpointEvidence[];\n readonly verifiedManifests: VerifiedAccessManifestCheckpointEvidence[];\n}\n\nexport function createProjectionCheckpointContext(input: {\n readonly execSql: ExecSql;\n readonly organizationId?: string | undefined;\n}): ProjectionCheckpointContext {\n if (typeof input.execSql !== \"function\") {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Projection verification requires durable keying checkpoint storage\",\n );\n }\n\n return {\n heldContainerHeads: [],\n execSql: input.execSql,\n localCheckpoints: new Map(),\n organizationId: input.organizationId,\n policies: [],\n authorizationPolicies: [],\n verifiedBundles: [],\n verifiedHeads: [],\n verifiedManifests: [],\n };\n}\n\nexport function observeAccessManifestCheckpoints(\n context: ProjectionCheckpointContext,\n input: {\n readonly verifiedHeads: readonly VerifiedAccessManifestCheckpointEvidence[];\n readonly verifiedManifests: readonly VerifiedAccessManifestCheckpointEvidence[];\n },\n): void {\n context.verifiedHeads.push(...input.verifiedHeads);\n context.verifiedManifests.push(...input.verifiedManifests);\n}\n\n/**\n * Records each served bundle whose manifest this verification produced, so\n * the transaction finalizing the checkpoints also stores it for later\n * citation. A bundle the projection carried but verification never reached\n * is skipped.\n */\nexport function observeVerifiedProjectionBundles(\n context: ProjectionCheckpointContext,\n bundlesByHash: ReadonlyMap<string, AccessManifestBundleWireResponse>,\n verifiedByHash: ReadonlyMap<\n string,\n { readonly checkpoint: AccessManifestCheckpoint }\n >,\n): void {\n for (const [manifestHash, bundle] of bundlesByHash) {\n const verified = verifiedByHash.get(manifestHash);\n // A cache hit verifies the hash, not these bytes: store only bytes that\n // a fresh verification accepted, so a citation never replays others.\n if (verified && matchesVerifiedBundleBytes(verified, bundle))\n context.verifiedBundles.push({ bundle, checkpoint: verified.checkpoint });\n }\n}\n\nexport function observePrincipalPolicy(\n context: ProjectionCheckpointContext,\n policy: PrincipalPolicyCheckpointEvidence,\n organizationId?: string | undefined,\n): void {\n if (\n organizationId !== undefined &&\n context.organizationId !== undefined &&\n context.organizationId !== organizationId\n ) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"projection checkpoint batch crosses organization ownership\",\n );\n }\n context.organizationId ??= organizationId;\n context.policies.push(policy);\n}\n\nexport async function commitProjectionCheckpoints(\n context: ProjectionCheckpointContext,\n input?: {\n readonly documentPurgeCheckpoint?: DocumentPurgeCheckpoint | undefined;\n readonly execSql?: ExecSql | undefined;\n readonly stillCurrent?: (() => boolean) | undefined;\n },\n): Promise<void> {\n input = {\n ...input,\n stillCurrent: projectionLifetimeGuard(context, input?.stillCurrent),\n };\n assertProjectionVerificationCurrent(input?.stillCurrent);\n await enforceAccessManifestCheckpoints({\n documentPurgeCheckpoint: input?.documentPurgeCheckpoint,\n execSql: input?.execSql ?? context.execSql,\n organizationId: context.organizationId,\n policies: context.policies,\n authorizationPolicies: context.authorizationPolicies,\n stillCurrent: input?.stillCurrent,\n verifiedBundles: context.verifiedBundles,\n verifiedHeads: context.verifiedHeads,\n verifiedManifests: context.verifiedManifests,\n }).catch((error: unknown) => {\n if (error instanceof PrincipalAuthorizationCheckpointUnavailableError)\n throw new ProjectionDependencyUnavailableError(error.message);\n throw error;\n });\n assertProjectionVerificationCurrent(input?.stillCurrent);\n}\n\n/** Validates against the latest durable pins without persisting new heads. */\nasync function validateProjectionCheckpoints(\n context: ProjectionCheckpointContext,\n input?: {\n readonly execSql?: ExecSql | undefined;\n readonly stillCurrent?: (() => boolean) | undefined;\n },\n): Promise<void> {\n input = {\n ...input,\n stillCurrent: projectionLifetimeGuard(context, input?.stillCurrent),\n };\n assertProjectionVerificationCurrent(input?.stillCurrent);\n await validateAccessManifestCheckpoints({\n execSql: input?.execSql ?? context.execSql,\n policies: context.policies,\n authorizationPolicies: context.authorizationPolicies,\n stillCurrent: input?.stillCurrent,\n verifiedBundles: context.verifiedBundles,\n verifiedHeads: context.verifiedHeads,\n verifiedManifests: context.verifiedManifests,\n }).catch((error: unknown) => {\n if (error instanceof PrincipalAuthorizationCheckpointUnavailableError)\n throw new ProjectionDependencyUnavailableError(error.message);\n throw error;\n });\n assertProjectionVerificationCurrent(input?.stillCurrent);\n}\n\nexport async function finalizeProjectionCheckpoints(\n context: ProjectionCheckpointContext,\n input: {\n readonly execSql?: ExecSql | undefined;\n readonly persistVerificationCheckpoints?: boolean | undefined;\n readonly stillCurrent?: (() => boolean) | undefined;\n },\n): Promise<void> {\n if (input.persistVerificationCheckpoints === false) {\n await validateProjectionCheckpoints(context, input);\n return;\n }\n await commitProjectionCheckpoints(context, input);\n if (input.stillCurrent?.() === false) return;\n const organizationId =\n context.organizationId ??\n context.heldContainerHeads[0]?.state.organizationId;\n if (organizationId === undefined) return;\n try {\n rememberVerifiedContainerHeads({\n organizationId,\n execSql: input.execSql ?? context.execSql,\n heads: context.heldContainerHeads,\n policies: context.policies,\n });\n } catch {\n // Checkpoints are already committed. Optional re-citation evidence must\n // never change successful verification into a caller-visible refusal.\n }\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import { serializeKeyingCanonicalJson } from "../../../internal/crypto/index.js";
2
+ import { readCanonicalJson } from "../../keying/canonicalJson.js";
3
+ // The exact bytes each manifest was verified from, keyed by the verified
4
+ // result. A later projection can hit the same verified hash while serving
5
+ // different bytes; only bytes that were themselves verified may be stored
6
+ // for citation.
7
+ const verifiedBundleBytes = new WeakMap();
8
+ function canonicalBytes(bundle) {
9
+ try {
10
+ return serializeKeyingCanonicalJson(readCanonicalJson(bundle, "Verified manifest bundle"));
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ }
16
+ /** Remember the bundle a fresh verification accepted for `verified`. */
17
+ export function recordVerifiedBundleBytes(verified, bundle) {
18
+ const bytes = canonicalBytes(bundle);
19
+ if (bytes !== null)
20
+ verifiedBundleBytes.set(verified, bytes);
21
+ }
22
+ /** Whether `bundle` is byte-identical to the one `verified` came from. */
23
+ export function matchesVerifiedBundleBytes(verified, bundle) {
24
+ const recorded = verifiedBundleBytes.get(verified);
25
+ return recorded !== undefined && recorded === canonicalBytes(bundle);
26
+ }
27
+ //# sourceMappingURL=verifiedBundleBytes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifiedBundleBytes.js","sourceRoot":"","sources":["verifiedBundleBytes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D,yEAAyE;AACzE,0EAA0E;AAC1E,0EAA0E;AAC1E,gBAAgB;AAChB,MAAM,mBAAmB,GAAG,IAAI,OAAO,EAAkB,CAAC;AAE1D,SAAS,cAAc,CAAC,MAAe;IACrC,IAAI,CAAC;QACH,OAAO,4BAA4B,CACjC,iBAAiB,CAAC,MAAM,EAAE,0BAA0B,CAAC,CACtD,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,yBAAyB,CACvC,QAAgB,EAChB,MAAe;IAEf,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,KAAK,KAAK,IAAI;QAAE,mBAAmB,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,0BAA0B,CACxC,QAAgB,EAChB,MAAe;IAEf,MAAM,QAAQ,GAAG,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnD,OAAO,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,cAAc,CAAC,MAAM,CAAC,CAAC;AACvE,CAAC","sourcesContent":["import { serializeKeyingCanonicalJson } from \"@tearleads/crypto\";\nimport { readCanonicalJson } from \"../../keying/canonicalJson\";\n\n// The exact bytes each manifest was verified from, keyed by the verified\n// result. A later projection can hit the same verified hash while serving\n// different bytes; only bytes that were themselves verified may be stored\n// for citation.\nconst verifiedBundleBytes = new WeakMap<object, string>();\n\nfunction canonicalBytes(bundle: unknown): string | null {\n try {\n return serializeKeyingCanonicalJson(\n readCanonicalJson(bundle, \"Verified manifest bundle\"),\n );\n } catch {\n return null;\n }\n}\n\n/** Remember the bundle a fresh verification accepted for `verified`. */\nexport function recordVerifiedBundleBytes(\n verified: object,\n bundle: unknown,\n): void {\n const bytes = canonicalBytes(bundle);\n if (bytes !== null) verifiedBundleBytes.set(verified, bytes);\n}\n\n/** Whether `bundle` is byte-identical to the one `verified` came from. */\nexport function matchesVerifiedBundleBytes(\n verified: object,\n bundle: unknown,\n): boolean {\n const recorded = verifiedBundleBytes.get(verified);\n return recorded !== undefined && recorded === canonicalBytes(bundle);\n}\n"]}
@@ -2,6 +2,7 @@ import { KeyingVerificationError, principalPolicyMatchesReference, verifyContain
2
2
  import { readCanonicalJson, readCanonicalRecord, } from "../../keying/canonicalJson.js";
3
3
  import { assertCanonicalEqual, verifyAccessEventBundle, } from "../checkpoints/bundleVerification.js";
4
4
  import { loadManifestCheckpointVerification, verifyCachedManifestCheckpoint, } from "../checkpoints/manifestCheckpointVerification.js";
5
+ import { recordVerifiedBundleBytes } from "../checkpoints/verifiedBundleBytes.js";
5
6
  import { readKeyingVerificationShape } from "../error.js";
6
7
  import { collectReferencedPrincipalPolicies } from "../principalPolicy/principalPolicyVerification.js";
7
8
  import { readAccessManifest, readRecordNullableString } from "../readers.js";
@@ -109,6 +110,7 @@ async function verifyFreshContainerManifestBundle(input, parentPath) {
109
110
  label: `${input.label} state`,
110
111
  });
111
112
  input.verifiedByHash.set(input.bundle.manifestHash, verified.value);
113
+ recordVerifiedBundleBytes(verified.value, input.bundle);
112
114
  return verified.value;
113
115
  }
114
116
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"containerManifestVerification.js","sourceRoot":"","sources":["containerManifestVerification.ts"],"names":[],"mappings":"AACA,OAAO,EACL,uBAAuB,EACvB,+BAA+B,EAE/B,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EACL,kCAAkC,EAClC,8BAA8B,GAC/B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAE,kCAAkC,EAAE,MAAM,gDAAgD,CAAC;AACpG,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAM1E,OAAO,EACL,gCAAgC,EAEhC,wBAAwB,GACzB,MAAM,8BAA8B,CAAC;AAEtC,KAAK,UAAU,oCAAoC,CAAC,KAanD;IACC,MAAM,qBAAqB,GAAG,KAAK,CAAC,wBAAwB,CAAC,MAAM,CACjE,CAAC,SAAS,EAAE,EAAE,CACZ,CAAC,KAAK,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAC3C,+BAA+B,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CACvD,CACJ,CAAC;IACF,IAAI,KAAK,CAAC,4BAA4B,IAAI,qBAAqB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,uBAAuB,CAC/B,oBAAoB,EACpB,qDAAqD,CACtD,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,4BAA4B;QACpC,OAAO,CAAC,GAAG,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC1C,OAAO;QACL,GAAG,KAAK,CAAC,qBAAqB;QAC9B,GAAG,CAAC,MAAM,kCAAkC,CAAC;YAC3C,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;YAC1C,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;YAChD,UAAU,EAAE,qBAAqB;YACjC,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,+BAA+B,EAAE,KAAK,CAAC,+BAA+B;SACvE,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAAC,KAuBnD;IACC,MAAM,UAAU,GACd,MAAM,8CAA8C,CAAC,KAAK,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACnE,IAAI,MAAM,EAAE,CAAC;QACX,gCAAgC,CAAC;YAC/B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,UAAU;YACV,gBAAgB,EAAE,MAAM;SACzB,CAAC,CAAC;QACH,IAAI,KAAK,CAAC,sBAAsB,EAAE,CAAC;YACjC,MAAM,8BAA8B,CAAC;gBACnC,OAAO,EAAE,MAAM;gBACf,OAAO,EAAE,KAAK,CAAC,iBAAiB,CAAC,OAAO;gBACxC,gBAAgB,EAAE,KAAK,CAAC,iBAAiB,CAAC,gBAAgB;gBAC1D,iBAAiB,EAAE,KAAK,CAAC,cAAc;aACxC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,GAAG,EAAU,CAAC;IACvD,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,uBAAuB,CAC/B,iBAAiB,EACjB,GAAG,KAAK,CAAC,KAAK,6DAA6D,CAC5E,CAAC;IACJ,CAAC;IACD,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,MAAM,kCAAkC,CAC7C,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,EACvB,UAAU,CACX,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC;AAID,KAAK,UAAU,kCAAkC,CAC/C,KAAyB,EACzB,UAAsD;IAEtD,MAAM,KAAK,GAAG,MAAM,uBAAuB,CAAC,KAAK,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,kBAAkB,CACjC,KAAK,CAAC,MAAM,CAAC,QAAQ,EACrB,GAAG,KAAK,CAAC,KAAK,WAAW,CAC1B,CAAC;IACF,MAAM,aAAa,GAAG,MAAM,qCAAqC,CAC/D,KAAK,EACL,KAAK,EACL,UAAU,CACX,CAAC;IACF,MAAM,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,GAAG,aAAa,CAAC;IAC5E,MAAM,iBAAiB,GAAG,MAAM,oCAAoC,CAAC;QACnE,qBAAqB,EAAE,KAAK,CAAC,qBAAqB,IAAI,EAAE;QACxD,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;QAC1C,cAAc,EAAE,KAAK,CAAC,KAAK,CAAC,cAAc;QAC1C,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;QAChD,wBAAwB,EAAE;YACxB,GAAG,aAAa,CAAC,wBAAwB;YACzC,GAAG,QAAQ,CAAC,wBAAwB;SACrC;QACD,4BAA4B,EAAE,KAAK,CAAC,4BAA4B,IAAI,KAAK;QACzE,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,+BAA+B,EAAE,KAAK,CAAC,+BAA+B;KACvE,CAAC,CAAC;IACH,MAAM,sBAAsB,GAAG,KAAK,CAAC,sBAAsB;QACzD,CAAC,CAAC,MAAM,kCAAkC,CAAC;YACvC,OAAO,EAAE,QAAQ;YACjB,OAAO,EAAE,KAAK,CAAC,iBAAiB,CAAC,OAAO;YACxC,gBAAgB,EAAE,KAAK,CAAC,iBAAiB,CAAC,gBAAgB;YAC1D,iBAAiB,EAAE,KAAK,CAAC,cAAc;SACxC,CAAC;QACJ,CAAC,CAAC,IAAI,CAAC;IAET,MAAM,QAAQ,GAAG,MAAM,6BAA6B,CAAC;QACnD,wEAAwE;QACxE,iEAAiE;QACjE,uBAAuB,EAAE,KAAK,CAAC,uBAAuB,IAAI,YAAY;QACtE,8BAA8B,EAAE,cAAc;QAC9C,KAAK;QACL,oBAAoB,EAAE,KAAK,CAAC,MAAM,CAAC,YAAY;QAC/C,QAAQ;QACR,mBAAmB,EAAE,cAAc;QACnC,iBAAiB;QACjB,GAAG,CAAC,sBAAsB,IAAI,EAAE,CAAC;QACjC,GAAG,CAAC,gBAAgB;YAClB,CAAC,CAAC;gBACE,qBAAqB,EAAE,CAAC,GAAG,eAAe,EAAE,gBAAgB,CAAC;gBAC7D,gBAAgB;aACjB;YACH,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;KAChC,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,uBAAuB,CAC/B,QAAQ,CAAC,KAAK,CAAC,IAAI,EACnB,GAAG,KAAK,CAAC,KAAK,kCAAkC,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,CACzE,CAAC;IACJ,CAAC;IAED,oBAAoB,CAAC;QACnB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK;QAC1B,QAAQ,EAAE,iBAAiB,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,KAAK,QAAQ,CAAC;QACzE,KAAK,EAAE,GAAG,KAAK,CAAC,KAAK,QAAQ;KAC9B,CAAC,CAAC;IACH,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEpE,OAAO,QAAQ,CAAC,KAAK,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,qCAAqC,CAClD,KAAyB,EACzB,KAA+C,EAC/C,UAAsD;IAEtD,MAAM,gBAAgB,GACpB,KAAK,CAAC,KAAK,CAAC,oBAAoB,KAAK,IAAI;QACvC,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,MAAM,+BAA+B,CAAC;YACpC,GAAG,KAAK;YACR,UAAU;YACV,oBAAoB,EAAE,KAAK,CAAC,KAAK,CAAC,oBAAoB;SACvD,CAAC,CAAC;IACT,8EAA8E;IAC9E,4EAA4E;IAC5E,8EAA8E;IAC9E,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC;IACxC,MAAM,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,GAC7C,gBAAgB,IAAI,SAAS,KAAK,gBAAgB;QAChD,CAAC,CAAC,gBAAgB,CAAC,KAAK;QACxB,CAAC,CAAC,qCAAqC,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAChE,IACE,gBAAgB,KAAK,IAAI;QACzB,kBAAkB,KAAK,IAAI;QAC3B,CAAC,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAClE,CAAC;QACD,MAAM,IAAI,uBAAuB,CAC/B,oBAAoB,EACpB,GAAG,KAAK,CAAC,KAAK,yDAAyD,CACxE,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAsB;QACjC,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,cAAc,EAAE,KAAK,CAAC,cAAc;KACrC,CAAC;IACF,MAAM,cAAc,GAAG,MAAM,qBAAqB,CAAC,KAAK,EAAE,KAAK,EAAE;QAC/D,iBAAiB;KAClB,CAAC,CAAC;IACH,gCAAgC,CAAC;QAC/B,GAAG,OAAO;QACV,cAAc;QACd,gBAAgB;KACjB,CAAC,CAAC;IACH,wEAAwE;IACxE,yEAAyE;IACzE,0DAA0D;IAC1D,MAAM,eAAe,GACnB,SAAS,KAAK,gBAAgB,IAAI,gBAAgB;QAChD,CAAC,CAAC,MAAM,qBAAqB,CAAC,KAAK,EAAE,KAAK,EAAE;YACxC,iBAAiB,EAAE,gBAAgB,CAAC,KAAK,CAAC,iBAAiB;SAC5D,CAAC;QACJ,CAAC,CAAC,cAAc,CAAC;IACrB,IAAI,eAAe,KAAK,cAAc,EAAE,CAAC;QACvC,gCAAgC,CAAC;YAC/B,GAAG,OAAO;YACV,cAAc,EAAE,eAAe;YAC/B,gBAAgB;SACjB,CAAC,CAAC;IACL,CAAC;IACD,MAAM,SAAS,GAAG;QAChB,GAAG,UAAU;QACb,GAAG,cAAc;QACjB,GAAG,CAAC,eAAe,KAAK,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC;KAC/D,CAAC;IACF,OAAO;QACL,cAAc;QACd,gBAAgB;QAChB,wBAAwB,EAAE;YACxB,GAAG,SAAS,CAAC,OAAO,CAClB,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,wBAAwB,CACtD;YACD,GAAG,CAAC,gBAAgB,EAAE,KAAK,CAAC,wBAAwB,IAAI,EAAE,CAAC;SAC5D;QACD,eAAe;KAChB,CAAC;AACJ,CAAC;AAED,SAAS,4BAA4B,CAAC,KAAyB;IAC7D,OAAO;QACL,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,yEAAyE;QACzE,yDAAyD;QACzD,mBAAmB,EAAE,CACnB,MAAwC,EACxC,KAAa,EACb,EAAE,CACF,6BAA6B,CAAC;YAC5B,GAAG,KAAK;YACR,uBAAuB,EAAE,YAAY;YACrC,MAAM;YACN,sBAAsB,EAAE,KAAK;YAC7B,KAAK;SACN,CAAC;KACL,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAC5B,KAAyB,EACzB,KAA+C,EAC/C,SAAwD;IAExD,OAAO,wBAAwB,CAAC;QAC9B,GAAG,4BAA4B,CAAC,KAAK,CAAC;QACtC,KAAK;QACL,iBAAiB,EAAE,SAAS,CAAC,iBAAiB;KAC/C,CAAC,CAAC;AACL,CAAC;AAED,SAAS,gCAAgC,CAAC,KAIzC;IACC,MAAM,wBAAwB,GAC5B,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,IAAI,IAAI,CAAC;IAChD,IACE,wBAAwB,KAAK,KAAK,CAAC,gBAAgB,CAAC,KAAK,CAAC,kBAAkB,EAC5E,CAAC;QACD,MAAM,IAAI,uBAAuB,CAC/B,iBAAiB,EACjB,GAAG,KAAK,CAAC,KAAK,uBAAuB,CACtC,CAAC;IACJ,CAAC;AACH,CAAC;AAOD,SAAS,mBAAmB,CAC1B,KAAc,EACd,KAAa;IAEb,OAAO,2BAA2B,CAAC,GAAG,EAAE;QACtC,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAEjD,OAAO;YACL,iBAAiB,EAAE,wBAAwB,CACzC,MAAM,EACN,mBAAmB,EACnB,KAAK,CACN;YACD,kBAAkB,EAAE,wBAAwB,CAC1C,MAAM,EACN,oBAAoB,EACpB,KAAK,CACN;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,oCAAoC,CAC3C,MAAwC,EACxC,KAAa;IAEb,OAAO,mBAAmB,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAC;AAC7D,CAAC;AAED,wEAAwE;AACxE,SAAS,qCAAqC,CAC5C,KAA+C,EAC/C,KAAa;IAEb,OAAO,mBAAmB,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,KAAK,aAAa,CAAC,CAAC;AAChE,CAAC;AAED,KAAK,UAAU,8CAA8C,CAAC,KAe7D;IACC,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAM,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,GAC7C,oCAAoC,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAClE,IAAI,iBAAiB,KAAK,IAAI,IAAI,kBAAkB,KAAK,IAAI,EAAE,CAAC;QAC9D,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,eAAe,GAAG,KAAK,CAAC,UAAU,CAAC,SAAS,CAChD,CAAC,QAAQ,EAAE,EAAE,CACX,QAAQ,CAAC,KAAK,CAAC,WAAW,KAAK,iBAAiB;QAChD,QAAQ,CAAC,YAAY,KAAK,kBAAkB,CAC/C,CAAC;IACF,IAAI,eAAe,IAAI,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,GAAG,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,YAAY,GAAG,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACjE,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,KAAK,CAAC,UAAU,CAAC;IAC1B,CAAC;IAED,MAAM,gBAAgB,GAAG,MAAM,8CAA8C,CAC3E;QACE,GAAG,KAAK;QACR,MAAM,EAAE,YAAY;QACpB,KAAK,EAAE,GAAG,KAAK,CAAC,KAAK,kBAAkB;KACxC,CACF,CAAC;IACF,MAAM,cAAc,GAAG,MAAM,6BAA6B,CAAC;QACzD,GAAG,KAAK;QACR,MAAM,EAAE,YAAY;QACpB,sBAAsB,EAAE,KAAK;QAC7B,KAAK,EAAE,GAAG,KAAK,CAAC,KAAK,kBAAkB;QACvC,UAAU,EAAE,gBAAgB;KAC7B,CAAC,CAAC;IACH,IAAI,cAAc,CAAC,KAAK,CAAC,WAAW,KAAK,iBAAiB,EAAE,CAAC;QAC3D,MAAM,IAAI,uBAAuB,CAC/B,iBAAiB,EACjB,GAAG,KAAK,CAAC,KAAK,qCAAqC,CACpD,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,GAAG,gBAAgB,EAAE,cAAc,CAAC,CAAC;AAC/C,CAAC;AAED,KAAK,UAAU,+BAA+B,CAAC,KAiB9C;IACC,MAAM,cAAc,GAAG,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAC3E,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,uBAAuB,CAC/B,oBAAoB,EACpB,GAAG,KAAK,CAAC,KAAK,sBAAsB,KAAK,CAAC,oBAAoB,aAAa,CAC5E,CAAC;IACJ,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,8CAA8C,CAAC;QACtE,GAAG,KAAK;QACR,MAAM,EAAE,cAAc;KACvB,CAAC,CAAC;IAEH,OAAO,6BAA6B,CAAC;QACnC,uBAAuB,EAAE,YAAY;QACrC,qBAAqB,EAAE,KAAK,CAAC,qBAAqB;QAClD,MAAM,EAAE,cAAc;QACtB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;QAC1C,sBAAsB,EAAE,KAAK;QAC7B,KAAK,EAAE,GAAG,KAAK,CAAC,KAAK,oBAAoB;QACzC,UAAU;QACV,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;QAChD,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,4BAA4B,EAAE,KAAK,CAAC,4BAA4B;QAChE,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,+BAA+B,EAAE,KAAK,CAAC,+BAA+B;KACvE,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { PrincipalPolicyAuthorization } from \"@tearleads/crypto\";\nimport {\n KeyingVerificationError,\n principalPolicyMatchesReference,\n type VerifiedContainerAccessManifest,\n verifyContainerAccessManifest,\n} from \"@tearleads/crypto\";\nimport type { AccessManifestBundleWireResponse } from \"@tearleads/validators/response\";\nimport {\n readCanonicalJson,\n readCanonicalRecord,\n} from \"../../keying/canonicalJson\";\nimport {\n assertCanonicalEqual,\n verifyAccessEventBundle,\n} from \"../checkpoints/bundleVerification\";\nimport type { ProjectionCheckpointContext } from \"../checkpoints/checkpointContext\";\nimport {\n loadManifestCheckpointVerification,\n verifyCachedManifestCheckpoint,\n} from \"../checkpoints/manifestCheckpointVerification\";\nimport { readKeyingVerificationShape } from \"../error\";\nimport { collectReferencedPrincipalPolicies } from \"../principalPolicy/principalPolicyVerification\";\nimport { readAccessManifest, readRecordNullableString } from \"../readers\";\nimport type {\n PrincipalPolicyCache,\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../types\";\nimport {\n assertCitedAncestorsDoNotRegress,\n type CitedLineageInput,\n resolveCitedAncestorPath,\n} from \"./containerAncestorCitations\";\n\nasync function collectManifestAuthorizationPolicies(input: {\n readonly authorizationEvidence: readonly PrincipalPolicyAuthorization[];\n readonly checkpointContext: ProjectionCheckpointContext;\n readonly organizationId: string;\n readonly principalPolicyCache: PrincipalPolicyCache;\n readonly referencedPrincipalHeads: Parameters<\n typeof principalPolicyMatchesReference\n >[0][\"reference\"][];\n readonly requireAuthorizationEvidence: boolean;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}) {\n const missingPrincipalHeads = input.referencedPrincipalHeads.filter(\n (reference) =>\n !input.authorizationEvidence.some((policy) =>\n principalPolicyMatchesReference({ policy, reference }),\n ),\n );\n if (input.requireAuthorizationEvidence && missingPrincipalHeads.length > 0) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Projection omits required principal policy evidence\",\n );\n }\n if (input.requireAuthorizationEvidence)\n return [...input.authorizationEvidence];\n return [\n ...input.authorizationEvidence,\n ...(await collectReferencedPrincipalPolicies({\n checkpointContext: input.checkpointContext,\n organizationId: input.organizationId,\n principalPolicyCache: input.principalPolicyCache,\n references: missingPrincipalHeads,\n resolveUserKey: input.resolveUserKey,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n })),\n ];\n}\n\nexport async function verifyContainerManifestBundle(input: {\n readonly authorizationMembership?: \"current\" | \"referenced\" | undefined;\n readonly authorizationEvidence?:\n | readonly PrincipalPolicyAuthorization[]\n | undefined;\n readonly bundle: AccessManifestBundleWireResponse;\n readonly bundlesByHash: ReadonlyMap<string, AccessManifestBundleWireResponse>;\n readonly checkpointContext: ProjectionCheckpointContext;\n readonly enforceLocalCheckpoint: boolean;\n readonly label: string;\n readonly parentPath: readonly VerifiedContainerAccessManifest[];\n readonly principalPolicyCache: PrincipalPolicyCache;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n readonly requireAuthorizationEvidence?: boolean | undefined;\n readonly verifiedByHash: Map<string, VerifiedContainerAccessManifest>;\n // Manifest hashes whose verification is in progress up the call stack.\n // Previous manifests, pinned parents, and cited ancestors all recurse\n // before a bundle's own hash is checked, so served bundles that refer to\n // each other under claimed hashes would otherwise recurse without end.\n readonly verifying?: Set<string> | undefined;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}): Promise<VerifiedContainerAccessManifest> {\n const parentPath =\n await resolveContainerManifestVerificationParentPath(input);\n const cached = input.verifiedByHash.get(input.bundle.manifestHash);\n if (cached) {\n assertContainerParentPathMatches({\n label: input.label,\n parentPath,\n verifiedManifest: cached,\n });\n if (input.enforceLocalCheckpoint) {\n await verifyCachedManifestCheckpoint({\n current: cached,\n execSql: input.checkpointContext.execSql,\n localCheckpoints: input.checkpointContext.localCheckpoints,\n verifiedManifests: input.verifiedByHash,\n });\n }\n return cached;\n }\n\n const verifying = input.verifying ?? new Set<string>();\n if (verifying.has(input.bundle.manifestHash)) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n `${input.label} refers back to a manifest whose verification it is part of`,\n );\n }\n verifying.add(input.bundle.manifestHash);\n try {\n return await verifyFreshContainerManifestBundle(\n { ...input, verifying },\n parentPath,\n );\n } finally {\n verifying.delete(input.bundle.manifestHash);\n }\n}\n\ntype CitedAncestorInput = Parameters<typeof verifyContainerManifestBundle>[0];\n\nasync function verifyFreshContainerManifestBundle(\n input: CitedAncestorInput,\n parentPath: readonly VerifiedContainerAccessManifest[],\n): Promise<VerifiedContainerAccessManifest> {\n const event = await verifyAccessEventBundle(input);\n const manifest = readAccessManifest(\n input.bundle.manifest,\n `${input.label} manifest`,\n );\n const authorization = await resolveContainerManifestAuthorization(\n input,\n event,\n parentPath,\n );\n const { citedAncestors, previousManifest, sourceAncestors } = authorization;\n const principalPolicies = await collectManifestAuthorizationPolicies({\n authorizationEvidence: input.authorizationEvidence ?? [],\n checkpointContext: input.checkpointContext,\n organizationId: event.event.organizationId,\n principalPolicyCache: input.principalPolicyCache,\n referencedPrincipalHeads: [\n ...authorization.referencedPrincipalHeads,\n ...manifest.referencedPrincipalHeads,\n ],\n requireAuthorizationEvidence: input.requireAuthorizationEvidence ?? false,\n resolveUserKey: input.resolveUserKey,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n const checkpointVerification = input.enforceLocalCheckpoint\n ? await loadManifestCheckpointVerification({\n current: manifest,\n execSql: input.checkpointContext.execSql,\n localCheckpoints: input.checkpointContext.localCheckpoints,\n verifiedManifests: input.verifiedByHash,\n })\n : null;\n\n const verified = await verifyContainerAccessManifest({\n // Served manifests were committed under their signed policy references.\n // Current membership still gates new writes in the API verifier.\n authorizationMembership: input.authorizationMembership ?? \"referenced\",\n destinationParentContainerPath: citedAncestors,\n event,\n expectedManifestHash: input.bundle.manifestHash,\n manifest,\n parentContainerPath: citedAncestors,\n principalPolicies,\n ...(checkpointVerification ?? {}),\n ...(previousManifest\n ? {\n previousContainerPath: [...sourceAncestors, previousManifest],\n previousManifest,\n }\n : { previousManifest: null }),\n });\n if (!verified.ok) {\n throw new KeyingVerificationError(\n verified.error.code,\n `${input.label} manifest verification failed: ${verified.error.message}`,\n );\n }\n\n assertCanonicalEqual({\n actual: input.bundle.state,\n expected: readCanonicalJson(verified.value.state, `${input.label} state`),\n label: `${input.label} state`,\n });\n input.verifiedByHash.set(input.bundle.manifestHash, verified.value);\n\n return verified.value;\n}\n\n/**\n * The paths a container event is authorized against: its previous manifest\n * and the ancestor heads its signed event cites, root to parent.\n */\nasync function resolveContainerManifestAuthorization(\n input: CitedAncestorInput,\n event: VerifiedContainerAccessManifest[\"event\"],\n parentPath: readonly VerifiedContainerAccessManifest[],\n) {\n const previousManifest =\n event.event.previousManifestHash === null\n ? null\n : await verifyPreviousContainerManifest({\n ...input,\n parentPath,\n previousManifestHash: event.event.previousManifestHash,\n });\n // Resolve the signed placement lineage, never the server-supplied wire state.\n // Rotations also resolve their new epoch's exact signed parent citation via\n // cited-ancestor verification below; that can differ from this previous path.\n const eventType = event.event.eventType;\n const { parentContainerId, parentManifestHash } =\n previousManifest && eventType !== \"container.move\"\n ? previousManifest.state\n : readContainerEventBodyParentReference(event, input.label);\n if (\n previousManifest === null &&\n parentManifestHash !== null &&\n !event.event.dependencyManifestHashes.includes(parentManifestHash)\n ) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n `${input.label} does not cite the parent manifest it was created under`,\n );\n }\n const lineage: CitedLineageInput = {\n bundlesByHash: input.bundlesByHash,\n label: input.label,\n verifiedByHash: input.verifiedByHash,\n };\n const citedAncestors = await resolveCitedAncestors(input, event, {\n parentContainerId,\n });\n assertCitedAncestorsDoNotRegress({\n ...lineage,\n citedAncestors,\n previousManifest,\n });\n // A move's source path leads to the previous manifest's parent, and the\n // move event cites that path too; the destination path is the one above.\n // The source path is held to the same no-regression rule.\n const sourceAncestors =\n eventType === \"container.move\" && previousManifest\n ? await resolveCitedAncestors(input, event, {\n parentContainerId: previousManifest.state.parentContainerId,\n })\n : citedAncestors;\n if (sourceAncestors !== citedAncestors) {\n assertCitedAncestorsDoNotRegress({\n ...lineage,\n citedAncestors: sourceAncestors,\n previousManifest,\n });\n }\n const ancestors = [\n ...parentPath,\n ...citedAncestors,\n ...(sourceAncestors === citedAncestors ? [] : sourceAncestors),\n ];\n return {\n citedAncestors,\n previousManifest,\n referencedPrincipalHeads: [\n ...ancestors.flatMap(\n (ancestor) => ancestor.state.referencedPrincipalHeads,\n ),\n ...(previousManifest?.state.referencedPrincipalHeads ?? []),\n ],\n sourceAncestors,\n };\n}\n\nfunction citedAncestorResolutionInput(input: CitedAncestorInput) {\n return {\n bundlesByHash: input.bundlesByHash,\n label: input.label,\n verifiedByHash: input.verifiedByHash,\n // A cited head older than the served one is history: authorize it at the\n // membership it referenced, as the previous manifest is.\n verifyHistoryBundle: (\n bundle: AccessManifestBundleWireResponse,\n label: string,\n ) =>\n verifyContainerManifestBundle({\n ...input,\n authorizationMembership: \"referenced\",\n bundle,\n enforceLocalCheckpoint: false,\n label,\n }),\n };\n}\n\nfunction resolveCitedAncestors(\n input: CitedAncestorInput,\n event: VerifiedContainerAccessManifest[\"event\"],\n reference: { readonly parentContainerId: string | null },\n): Promise<VerifiedContainerAccessManifest[]> {\n return resolveCitedAncestorPath({\n ...citedAncestorResolutionInput(input),\n event,\n parentContainerId: reference.parentContainerId,\n });\n}\n\nfunction assertContainerParentPathMatches(input: {\n readonly label: string;\n readonly parentPath: readonly VerifiedContainerAccessManifest[];\n readonly verifiedManifest: VerifiedContainerAccessManifest;\n}): void {\n const actualParentManifestHash =\n input.parentPath.at(-1)?.manifestHash ?? null;\n if (\n actualParentManifestHash !== input.verifiedManifest.state.parentManifestHash\n ) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n `${input.label} parent path mismatch`,\n );\n }\n}\n\ninterface ContainerParentReference {\n parentContainerId: string | null;\n parentManifestHash: string | null;\n}\n\nfunction readParentReference(\n value: unknown,\n label: string,\n): ContainerParentReference {\n return readKeyingVerificationShape(() => {\n const record = readCanonicalRecord(value, label);\n\n return {\n parentContainerId: readRecordNullableString(\n record,\n \"parentContainerId\",\n label,\n ),\n parentManifestHash: readRecordNullableString(\n record,\n \"parentManifestHash\",\n label,\n ),\n };\n });\n}\n\nfunction readContainerManifestParentReference(\n bundle: AccessManifestBundleWireResponse,\n label: string,\n): ContainerParentReference {\n return readParentReference(bundle.state, `${label} state`);\n}\n\n/** The parent a create or move body pins, read from the signed body. */\nfunction readContainerEventBodyParentReference(\n event: VerifiedContainerAccessManifest[\"event\"],\n label: string,\n): ContainerParentReference {\n return readParentReference(event.body, `${label} event body`);\n}\n\nasync function resolveContainerManifestVerificationParentPath(input: {\n readonly authorizationEvidence?:\n | readonly PrincipalPolicyAuthorization[]\n | undefined;\n readonly bundle: AccessManifestBundleWireResponse;\n readonly bundlesByHash: ReadonlyMap<string, AccessManifestBundleWireResponse>;\n readonly checkpointContext: ProjectionCheckpointContext;\n readonly label: string;\n readonly parentPath: readonly VerifiedContainerAccessManifest[];\n readonly principalPolicyCache: PrincipalPolicyCache;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n readonly verifiedByHash: Map<string, VerifiedContainerAccessManifest>;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}): Promise<readonly VerifiedContainerAccessManifest[]> {\n // Descendants keep the parent manifest hash they were created or moved under;\n // a later parent share/rekey must not require rewriting descendant manifests.\n const { parentContainerId, parentManifestHash } =\n readContainerManifestParentReference(input.bundle, input.label);\n if (parentContainerId === null || parentManifestHash === null) {\n return [];\n }\n\n const parentPathIndex = input.parentPath.findIndex(\n (manifest) =>\n manifest.state.containerId === parentContainerId &&\n manifest.manifestHash === parentManifestHash,\n );\n if (parentPathIndex >= 0) {\n return input.parentPath.slice(0, parentPathIndex + 1);\n }\n\n const parentBundle = input.bundlesByHash.get(parentManifestHash);\n if (!parentBundle) {\n return input.parentPath;\n }\n\n const parentParentPath = await resolveContainerManifestVerificationParentPath(\n {\n ...input,\n bundle: parentBundle,\n label: `${input.label} parent manifest`,\n },\n );\n const verifiedParent = await verifyContainerManifestBundle({\n ...input,\n bundle: parentBundle,\n enforceLocalCheckpoint: false,\n label: `${input.label} parent manifest`,\n parentPath: parentParentPath,\n });\n if (verifiedParent.state.containerId !== parentContainerId) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n `${input.label} parent manifest container mismatch`,\n );\n }\n\n return [...parentParentPath, verifiedParent];\n}\n\nasync function verifyPreviousContainerManifest(input: {\n readonly authorizationEvidence?:\n | readonly PrincipalPolicyAuthorization[]\n | undefined;\n readonly bundlesByHash: ReadonlyMap<string, AccessManifestBundleWireResponse>;\n readonly checkpointContext: ProjectionCheckpointContext;\n readonly label: string;\n readonly parentPath: readonly VerifiedContainerAccessManifest[];\n readonly principalPolicyCache: PrincipalPolicyCache;\n readonly previousManifestHash: string;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n readonly requireAuthorizationEvidence?: boolean | undefined;\n readonly verifiedByHash: Map<string, VerifiedContainerAccessManifest>;\n readonly verifying?: Set<string> | undefined;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}): Promise<VerifiedContainerAccessManifest> {\n const previousBundle = input.bundlesByHash.get(input.previousManifestHash);\n if (!previousBundle) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n `${input.label} previous manifest ${input.previousManifestHash} is missing`,\n );\n }\n const parentPath = await resolveContainerManifestVerificationParentPath({\n ...input,\n bundle: previousBundle,\n });\n\n return verifyContainerManifestBundle({\n authorizationMembership: \"referenced\",\n authorizationEvidence: input.authorizationEvidence,\n bundle: previousBundle,\n bundlesByHash: input.bundlesByHash,\n checkpointContext: input.checkpointContext,\n enforceLocalCheckpoint: false,\n label: `${input.label} previous manifest`,\n parentPath,\n principalPolicyCache: input.principalPolicyCache,\n resolveUserKey: input.resolveUserKey,\n requireAuthorizationEvidence: input.requireAuthorizationEvidence,\n verifiedByHash: input.verifiedByHash,\n verifying: input.verifying,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n}\n"]}
1
+ {"version":3,"file":"containerManifestVerification.js","sourceRoot":"","sources":["containerManifestVerification.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PrincipalPolicyAuthorization } from \"@tearleads/crypto\";\nimport {\n KeyingVerificationError,\n principalPolicyMatchesReference,\n type VerifiedContainerAccessManifest,\n verifyContainerAccessManifest,\n} from \"@tearleads/crypto\";\nimport type { AccessManifestBundleWireResponse } from \"@tearleads/validators/response\";\nimport {\n readCanonicalJson,\n readCanonicalRecord,\n} from \"../../keying/canonicalJson\";\nimport {\n assertCanonicalEqual,\n verifyAccessEventBundle,\n} from \"../checkpoints/bundleVerification\";\nimport type { ProjectionCheckpointContext } from \"../checkpoints/checkpointContext\";\nimport {\n loadManifestCheckpointVerification,\n verifyCachedManifestCheckpoint,\n} from \"../checkpoints/manifestCheckpointVerification\";\nimport { recordVerifiedBundleBytes } from \"../checkpoints/verifiedBundleBytes\";\nimport { readKeyingVerificationShape } from \"../error\";\nimport { collectReferencedPrincipalPolicies } from \"../principalPolicy/principalPolicyVerification\";\nimport { readAccessManifest, readRecordNullableString } from \"../readers\";\nimport type {\n PrincipalPolicyCache,\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../types\";\nimport {\n assertCitedAncestorsDoNotRegress,\n type CitedLineageInput,\n resolveCitedAncestorPath,\n} from \"./containerAncestorCitations\";\n\nasync function collectManifestAuthorizationPolicies(input: {\n readonly authorizationEvidence: readonly PrincipalPolicyAuthorization[];\n readonly checkpointContext: ProjectionCheckpointContext;\n readonly organizationId: string;\n readonly principalPolicyCache: PrincipalPolicyCache;\n readonly referencedPrincipalHeads: Parameters<\n typeof principalPolicyMatchesReference\n >[0][\"reference\"][];\n readonly requireAuthorizationEvidence: boolean;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}) {\n const missingPrincipalHeads = input.referencedPrincipalHeads.filter(\n (reference) =>\n !input.authorizationEvidence.some((policy) =>\n principalPolicyMatchesReference({ policy, reference }),\n ),\n );\n if (input.requireAuthorizationEvidence && missingPrincipalHeads.length > 0) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Projection omits required principal policy evidence\",\n );\n }\n if (input.requireAuthorizationEvidence)\n return [...input.authorizationEvidence];\n return [\n ...input.authorizationEvidence,\n ...(await collectReferencedPrincipalPolicies({\n checkpointContext: input.checkpointContext,\n organizationId: input.organizationId,\n principalPolicyCache: input.principalPolicyCache,\n references: missingPrincipalHeads,\n resolveUserKey: input.resolveUserKey,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n })),\n ];\n}\n\nexport async function verifyContainerManifestBundle(input: {\n readonly authorizationMembership?: \"current\" | \"referenced\" | undefined;\n readonly authorizationEvidence?:\n | readonly PrincipalPolicyAuthorization[]\n | undefined;\n readonly bundle: AccessManifestBundleWireResponse;\n readonly bundlesByHash: ReadonlyMap<string, AccessManifestBundleWireResponse>;\n readonly checkpointContext: ProjectionCheckpointContext;\n readonly enforceLocalCheckpoint: boolean;\n readonly label: string;\n readonly parentPath: readonly VerifiedContainerAccessManifest[];\n readonly principalPolicyCache: PrincipalPolicyCache;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n readonly requireAuthorizationEvidence?: boolean | undefined;\n readonly verifiedByHash: Map<string, VerifiedContainerAccessManifest>;\n // Manifest hashes whose verification is in progress up the call stack.\n // Previous manifests, pinned parents, and cited ancestors all recurse\n // before a bundle's own hash is checked, so served bundles that refer to\n // each other under claimed hashes would otherwise recurse without end.\n readonly verifying?: Set<string> | undefined;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}): Promise<VerifiedContainerAccessManifest> {\n const parentPath =\n await resolveContainerManifestVerificationParentPath(input);\n const cached = input.verifiedByHash.get(input.bundle.manifestHash);\n if (cached) {\n assertContainerParentPathMatches({\n label: input.label,\n parentPath,\n verifiedManifest: cached,\n });\n if (input.enforceLocalCheckpoint) {\n await verifyCachedManifestCheckpoint({\n current: cached,\n execSql: input.checkpointContext.execSql,\n localCheckpoints: input.checkpointContext.localCheckpoints,\n verifiedManifests: input.verifiedByHash,\n });\n }\n return cached;\n }\n\n const verifying = input.verifying ?? new Set<string>();\n if (verifying.has(input.bundle.manifestHash)) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n `${input.label} refers back to a manifest whose verification it is part of`,\n );\n }\n verifying.add(input.bundle.manifestHash);\n try {\n return await verifyFreshContainerManifestBundle(\n { ...input, verifying },\n parentPath,\n );\n } finally {\n verifying.delete(input.bundle.manifestHash);\n }\n}\n\ntype CitedAncestorInput = Parameters<typeof verifyContainerManifestBundle>[0];\n\nasync function verifyFreshContainerManifestBundle(\n input: CitedAncestorInput,\n parentPath: readonly VerifiedContainerAccessManifest[],\n): Promise<VerifiedContainerAccessManifest> {\n const event = await verifyAccessEventBundle(input);\n const manifest = readAccessManifest(\n input.bundle.manifest,\n `${input.label} manifest`,\n );\n const authorization = await resolveContainerManifestAuthorization(\n input,\n event,\n parentPath,\n );\n const { citedAncestors, previousManifest, sourceAncestors } = authorization;\n const principalPolicies = await collectManifestAuthorizationPolicies({\n authorizationEvidence: input.authorizationEvidence ?? [],\n checkpointContext: input.checkpointContext,\n organizationId: event.event.organizationId,\n principalPolicyCache: input.principalPolicyCache,\n referencedPrincipalHeads: [\n ...authorization.referencedPrincipalHeads,\n ...manifest.referencedPrincipalHeads,\n ],\n requireAuthorizationEvidence: input.requireAuthorizationEvidence ?? false,\n resolveUserKey: input.resolveUserKey,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n const checkpointVerification = input.enforceLocalCheckpoint\n ? await loadManifestCheckpointVerification({\n current: manifest,\n execSql: input.checkpointContext.execSql,\n localCheckpoints: input.checkpointContext.localCheckpoints,\n verifiedManifests: input.verifiedByHash,\n })\n : null;\n\n const verified = await verifyContainerAccessManifest({\n // Served manifests were committed under their signed policy references.\n // Current membership still gates new writes in the API verifier.\n authorizationMembership: input.authorizationMembership ?? \"referenced\",\n destinationParentContainerPath: citedAncestors,\n event,\n expectedManifestHash: input.bundle.manifestHash,\n manifest,\n parentContainerPath: citedAncestors,\n principalPolicies,\n ...(checkpointVerification ?? {}),\n ...(previousManifest\n ? {\n previousContainerPath: [...sourceAncestors, previousManifest],\n previousManifest,\n }\n : { previousManifest: null }),\n });\n if (!verified.ok) {\n throw new KeyingVerificationError(\n verified.error.code,\n `${input.label} manifest verification failed: ${verified.error.message}`,\n );\n }\n\n assertCanonicalEqual({\n actual: input.bundle.state,\n expected: readCanonicalJson(verified.value.state, `${input.label} state`),\n label: `${input.label} state`,\n });\n input.verifiedByHash.set(input.bundle.manifestHash, verified.value);\n recordVerifiedBundleBytes(verified.value, input.bundle);\n\n return verified.value;\n}\n\n/**\n * The paths a container event is authorized against: its previous manifest\n * and the ancestor heads its signed event cites, root to parent.\n */\nasync function resolveContainerManifestAuthorization(\n input: CitedAncestorInput,\n event: VerifiedContainerAccessManifest[\"event\"],\n parentPath: readonly VerifiedContainerAccessManifest[],\n) {\n const previousManifest =\n event.event.previousManifestHash === null\n ? null\n : await verifyPreviousContainerManifest({\n ...input,\n parentPath,\n previousManifestHash: event.event.previousManifestHash,\n });\n // Resolve the signed placement lineage, never the server-supplied wire state.\n // Rotations also resolve their new epoch's exact signed parent citation via\n // cited-ancestor verification below; that can differ from this previous path.\n const eventType = event.event.eventType;\n const { parentContainerId, parentManifestHash } =\n previousManifest && eventType !== \"container.move\"\n ? previousManifest.state\n : readContainerEventBodyParentReference(event, input.label);\n if (\n previousManifest === null &&\n parentManifestHash !== null &&\n !event.event.dependencyManifestHashes.includes(parentManifestHash)\n ) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n `${input.label} does not cite the parent manifest it was created under`,\n );\n }\n const lineage: CitedLineageInput = {\n bundlesByHash: input.bundlesByHash,\n label: input.label,\n verifiedByHash: input.verifiedByHash,\n };\n const citedAncestors = await resolveCitedAncestors(input, event, {\n parentContainerId,\n });\n assertCitedAncestorsDoNotRegress({\n ...lineage,\n citedAncestors,\n previousManifest,\n });\n // A move's source path leads to the previous manifest's parent, and the\n // move event cites that path too; the destination path is the one above.\n // The source path is held to the same no-regression rule.\n const sourceAncestors =\n eventType === \"container.move\" && previousManifest\n ? await resolveCitedAncestors(input, event, {\n parentContainerId: previousManifest.state.parentContainerId,\n })\n : citedAncestors;\n if (sourceAncestors !== citedAncestors) {\n assertCitedAncestorsDoNotRegress({\n ...lineage,\n citedAncestors: sourceAncestors,\n previousManifest,\n });\n }\n const ancestors = [\n ...parentPath,\n ...citedAncestors,\n ...(sourceAncestors === citedAncestors ? [] : sourceAncestors),\n ];\n return {\n citedAncestors,\n previousManifest,\n referencedPrincipalHeads: [\n ...ancestors.flatMap(\n (ancestor) => ancestor.state.referencedPrincipalHeads,\n ),\n ...(previousManifest?.state.referencedPrincipalHeads ?? []),\n ],\n sourceAncestors,\n };\n}\n\nfunction citedAncestorResolutionInput(input: CitedAncestorInput) {\n return {\n bundlesByHash: input.bundlesByHash,\n label: input.label,\n verifiedByHash: input.verifiedByHash,\n // A cited head older than the served one is history: authorize it at the\n // membership it referenced, as the previous manifest is.\n verifyHistoryBundle: (\n bundle: AccessManifestBundleWireResponse,\n label: string,\n ) =>\n verifyContainerManifestBundle({\n ...input,\n authorizationMembership: \"referenced\",\n bundle,\n enforceLocalCheckpoint: false,\n label,\n }),\n };\n}\n\nfunction resolveCitedAncestors(\n input: CitedAncestorInput,\n event: VerifiedContainerAccessManifest[\"event\"],\n reference: { readonly parentContainerId: string | null },\n): Promise<VerifiedContainerAccessManifest[]> {\n return resolveCitedAncestorPath({\n ...citedAncestorResolutionInput(input),\n event,\n parentContainerId: reference.parentContainerId,\n });\n}\n\nfunction assertContainerParentPathMatches(input: {\n readonly label: string;\n readonly parentPath: readonly VerifiedContainerAccessManifest[];\n readonly verifiedManifest: VerifiedContainerAccessManifest;\n}): void {\n const actualParentManifestHash =\n input.parentPath.at(-1)?.manifestHash ?? null;\n if (\n actualParentManifestHash !== input.verifiedManifest.state.parentManifestHash\n ) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n `${input.label} parent path mismatch`,\n );\n }\n}\n\ninterface ContainerParentReference {\n parentContainerId: string | null;\n parentManifestHash: string | null;\n}\n\nfunction readParentReference(\n value: unknown,\n label: string,\n): ContainerParentReference {\n return readKeyingVerificationShape(() => {\n const record = readCanonicalRecord(value, label);\n\n return {\n parentContainerId: readRecordNullableString(\n record,\n \"parentContainerId\",\n label,\n ),\n parentManifestHash: readRecordNullableString(\n record,\n \"parentManifestHash\",\n label,\n ),\n };\n });\n}\n\nfunction readContainerManifestParentReference(\n bundle: AccessManifestBundleWireResponse,\n label: string,\n): ContainerParentReference {\n return readParentReference(bundle.state, `${label} state`);\n}\n\n/** The parent a create or move body pins, read from the signed body. */\nfunction readContainerEventBodyParentReference(\n event: VerifiedContainerAccessManifest[\"event\"],\n label: string,\n): ContainerParentReference {\n return readParentReference(event.body, `${label} event body`);\n}\n\nasync function resolveContainerManifestVerificationParentPath(input: {\n readonly authorizationEvidence?:\n | readonly PrincipalPolicyAuthorization[]\n | undefined;\n readonly bundle: AccessManifestBundleWireResponse;\n readonly bundlesByHash: ReadonlyMap<string, AccessManifestBundleWireResponse>;\n readonly checkpointContext: ProjectionCheckpointContext;\n readonly label: string;\n readonly parentPath: readonly VerifiedContainerAccessManifest[];\n readonly principalPolicyCache: PrincipalPolicyCache;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n readonly verifiedByHash: Map<string, VerifiedContainerAccessManifest>;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}): Promise<readonly VerifiedContainerAccessManifest[]> {\n // Descendants keep the parent manifest hash they were created or moved under;\n // a later parent share/rekey must not require rewriting descendant manifests.\n const { parentContainerId, parentManifestHash } =\n readContainerManifestParentReference(input.bundle, input.label);\n if (parentContainerId === null || parentManifestHash === null) {\n return [];\n }\n\n const parentPathIndex = input.parentPath.findIndex(\n (manifest) =>\n manifest.state.containerId === parentContainerId &&\n manifest.manifestHash === parentManifestHash,\n );\n if (parentPathIndex >= 0) {\n return input.parentPath.slice(0, parentPathIndex + 1);\n }\n\n const parentBundle = input.bundlesByHash.get(parentManifestHash);\n if (!parentBundle) {\n return input.parentPath;\n }\n\n const parentParentPath = await resolveContainerManifestVerificationParentPath(\n {\n ...input,\n bundle: parentBundle,\n label: `${input.label} parent manifest`,\n },\n );\n const verifiedParent = await verifyContainerManifestBundle({\n ...input,\n bundle: parentBundle,\n enforceLocalCheckpoint: false,\n label: `${input.label} parent manifest`,\n parentPath: parentParentPath,\n });\n if (verifiedParent.state.containerId !== parentContainerId) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n `${input.label} parent manifest container mismatch`,\n );\n }\n\n return [...parentParentPath, verifiedParent];\n}\n\nasync function verifyPreviousContainerManifest(input: {\n readonly authorizationEvidence?:\n | readonly PrincipalPolicyAuthorization[]\n | undefined;\n readonly bundlesByHash: ReadonlyMap<string, AccessManifestBundleWireResponse>;\n readonly checkpointContext: ProjectionCheckpointContext;\n readonly label: string;\n readonly parentPath: readonly VerifiedContainerAccessManifest[];\n readonly principalPolicyCache: PrincipalPolicyCache;\n readonly previousManifestHash: string;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n readonly requireAuthorizationEvidence?: boolean | undefined;\n readonly verifiedByHash: Map<string, VerifiedContainerAccessManifest>;\n readonly verifying?: Set<string> | undefined;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}): Promise<VerifiedContainerAccessManifest> {\n const previousBundle = input.bundlesByHash.get(input.previousManifestHash);\n if (!previousBundle) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n `${input.label} previous manifest ${input.previousManifestHash} is missing`,\n );\n }\n const parentPath = await resolveContainerManifestVerificationParentPath({\n ...input,\n bundle: previousBundle,\n });\n\n return verifyContainerManifestBundle({\n authorizationMembership: \"referenced\",\n authorizationEvidence: input.authorizationEvidence,\n bundle: previousBundle,\n bundlesByHash: input.bundlesByHash,\n checkpointContext: input.checkpointContext,\n enforceLocalCheckpoint: false,\n label: `${input.label} previous manifest`,\n parentPath,\n principalPolicyCache: input.principalPolicyCache,\n resolveUserKey: input.resolveUserKey,\n requireAuthorizationEvidence: input.requireAuthorizationEvidence,\n verifiedByHash: input.verifiedByHash,\n verifying: input.verifying,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n}\n"]}
@@ -2,6 +2,7 @@ import { KeyingVerificationError, verifyDocumentLinkSetManifest, } from "../../.
2
2
  import { readCanonicalJson } from "../../keying/canonicalJson.js";
3
3
  import { addBundleByHash, assertCanonicalEqual, verifyAccessEventBundle, } from "../checkpoints/bundleVerification.js";
4
4
  import { loadManifestCheckpointVerification, verifyCachedManifestCheckpoint, } from "../checkpoints/manifestCheckpointVerification.js";
5
+ import { recordVerifiedBundleBytes } from "../checkpoints/verifiedBundleBytes.js";
5
6
  import { readAccessManifest, readDocumentAccessEventBody } from "../readers.js";
6
7
  import { resolveEventContainerPaths } from "./documentDependencyPaths.js";
7
8
  import { collectDocumentManifestPrincipalPolicies, recordUsedDocumentContainerManifests, } from "./documentManifestPolicies.js";
@@ -78,6 +79,7 @@ export async function verifyDocumentManifestBundle(input) {
78
79
  label: `${input.label} state`,
79
80
  });
80
81
  input.verifiedByHash.set(input.bundle.manifestHash, verified.value);
82
+ recordVerifiedBundleBytes(verified.value, input.bundle);
81
83
  return verified.value;
82
84
  }
83
85
  export async function verifyDocumentProjectionManifests(input) {
@@ -1 +1 @@
1
- {"version":3,"file":"documentManifestVerification.js","sourceRoot":"","sources":["documentManifestVerification.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PrincipalPolicyAuthorization } from \"@tearleads/crypto\";\nimport {\n KeyingVerificationError,\n type VerifiedContainerAccessManifest,\n type VerifiedDocumentLinkSetManifest,\n type VerifiedDocumentLinkSetSnapshot,\n verifyDocumentLinkSetManifest,\n} from \"@tearleads/crypto\";\nimport type {\n AccessManifestBundleWireResponse,\n DocumentWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { readCanonicalJson } from \"../../keying/canonicalJson\";\nimport {\n addBundleByHash,\n assertCanonicalEqual,\n verifyAccessEventBundle,\n} from \"../checkpoints/bundleVerification\";\nimport type { ProjectionCheckpointContext } from \"../checkpoints/checkpointContext\";\nimport {\n loadManifestCheckpointVerification,\n verifyCachedManifestCheckpoint,\n} from \"../checkpoints/manifestCheckpointVerification\";\nimport { readAccessManifest, readDocumentAccessEventBody } from \"../readers\";\nimport type {\n PrincipalPolicyCache,\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../types\";\nimport { resolveEventContainerPaths } from \"./documentDependencyPaths\";\nimport {\n collectDocumentManifestPrincipalPolicies,\n recordUsedDocumentContainerManifests,\n type UsedDocumentContainerManifests,\n} from \"./documentManifestPolicies\";\nimport { requireVerifiedDocumentPredecessor } from \"./documentManifestPredecessor\";\n\ntype PolicyWarmer = ReferencedPrincipalPolicyWarmer | undefined;\n\nexport async function verifyDocumentManifestBundle(input: {\n readonly authorizationMembership?: \"current\" | \"referenced\" | undefined;\n readonly authorizationEvidence?:\n | readonly PrincipalPolicyAuthorization[]\n | undefined;\n readonly bundle: AccessManifestBundleWireResponse;\n readonly bundlesByHash: ReadonlyMap<string, AccessManifestBundleWireResponse>;\n readonly containerPathByManifestHash: ReadonlyMap<\n string,\n readonly VerifiedContainerAccessManifest[]\n >;\n readonly checkpointContext: ProjectionCheckpointContext;\n readonly enforceLocalCheckpoint: boolean;\n readonly label: string;\n readonly principalPolicyCache: PrincipalPolicyCache;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n readonly requireAuthorizationEvidence?: boolean | undefined;\n readonly trustedPredecessorByHash?:\n | ReadonlyMap<string, VerifiedDocumentLinkSetSnapshot>\n | undefined;\n readonly usedContainerManifests?: UsedDocumentContainerManifests | undefined;\n readonly verifiedByHash: Map<string, VerifiedDocumentLinkSetManifest>;\n readonly warmReferencedPrincipalPolicies?: PolicyWarmer;\n}): Promise<VerifiedDocumentLinkSetManifest> {\n const cached = input.verifiedByHash.get(input.bundle.manifestHash);\n if (cached) {\n if (input.enforceLocalCheckpoint) {\n await verifyCachedManifestCheckpoint({\n current: cached,\n execSql: input.checkpointContext.execSql,\n localCheckpoints: input.checkpointContext.localCheckpoints,\n verifiedManifests: input.verifiedByHash,\n });\n }\n return cached;\n }\n\n const event = await verifyAccessEventBundle(input);\n const manifest = readAccessManifest(\n input.bundle.manifest,\n `${input.label} manifest`,\n );\n const previousManifest = requireVerifiedDocumentPredecessor({\n label: input.label,\n previousManifestHash: event.event.previousManifestHash,\n trustedPredecessorByHash: input.trustedPredecessorByHash,\n verifiedByHash: input.verifiedByHash,\n });\n const body = readDocumentAccessEventBody(\n event.body,\n `${input.label} event body`,\n );\n const { dependencyContainerPaths, targetContainerPath } =\n resolveEventContainerPaths({\n containerPathByManifestHash: input.containerPathByManifestHash,\n dependencyManifestHashes: event.event.dependencyManifestHashes,\n targetManifestHash: body.containerManifestHash,\n });\n const principalPolicies = await collectDocumentManifestPrincipalPolicies({\n authorizationEvidence: input.authorizationEvidence,\n checkpointContext: input.checkpointContext,\n organizationId: event.event.organizationId,\n paths: [...dependencyContainerPaths, targetContainerPath],\n principalPolicyCache: input.principalPolicyCache,\n resolveUserKey: input.resolveUserKey,\n requireAuthorizationEvidence: input.requireAuthorizationEvidence,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n const checkpointVerification = input.enforceLocalCheckpoint\n ? await loadManifestCheckpointVerification({\n current: manifest,\n execSql: input.checkpointContext.execSql,\n localCheckpoints: input.checkpointContext.localCheckpoints,\n verifiedManifests: input.verifiedByHash,\n })\n : null;\n const verified = await verifyDocumentLinkSetManifest({\n // Served heads and history were committed under the group membership their\n // cited container heads referenced; a signer removed since must still\n // verify on a cold device. The API verifier gates new writes at current.\n authorizationMembership: input.authorizationMembership ?? \"referenced\",\n authorizingContainerPaths: dependencyContainerPaths,\n event,\n expectedManifestHash: input.bundle.manifestHash,\n manifest,\n previousManifest,\n principalPolicies,\n ...(checkpointVerification ?? {}),\n ...(targetContainerPath ? { targetContainerPath } : {}),\n });\n if (!verified.ok) {\n throw new KeyingVerificationError(\n verified.error.code,\n `${input.label} manifest verification failed: ${verified.error.message}`,\n );\n }\n\n recordUsedDocumentContainerManifests({\n paths: [...dependencyContainerPaths, targetContainerPath],\n used: input.usedContainerManifests,\n });\n assertCanonicalEqual({\n actual: input.bundle.state,\n expected: readCanonicalJson(verified.value.state, `${input.label} state`),\n label: `${input.label} state`,\n });\n input.verifiedByHash.set(input.bundle.manifestHash, verified.value);\n\n return verified.value;\n}\n\nexport async function verifyDocumentProjectionManifests(\n input: Omit<\n Parameters<typeof verifyDocumentManifestBundle>[0],\n | \"bundle\"\n | \"bundlesByHash\"\n | \"enforceLocalCheckpoint\"\n | \"label\"\n | \"verifiedByHash\"\n > & { readonly projection: DocumentWriterProjectionResponse },\n) {\n const bundlesByHash = new Map<string, AccessManifestBundleWireResponse>();\n addBundleByHash(\n bundlesByHash,\n input.projection.documentManifest,\n \"Document writer projection manifest\",\n );\n const history = input.projection.documentManifestHistory;\n for (const [index, bundle] of history.entries()) {\n addBundleByHash(\n bundlesByHash,\n bundle,\n `Document writer projection manifest history[${index}]`,\n );\n }\n // An honest API never lists the head in its own history (it seeds the walk\n // with the head), so a repeat is a malformed or tampered projection and is\n // refused as such, rather than letting the head reach its verification\n // through the history cache and relying on the cached branch to re-run\n // every check the fresh path would.\n if (\n history.some(\n (bundle) =>\n bundle.manifestHash === input.projection.documentManifest.manifestHash,\n )\n ) {\n throw new KeyingVerificationError(\n \"duplicate_entry\",\n \"Document writer projection history repeats the current head\",\n );\n }\n\n const verifiedByHash = new Map<string, VerifiedDocumentLinkSetManifest>();\n for (let index = history.length - 1; index >= 0; index -= 1) {\n const bundle = history[index];\n if (!bundle) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n `Document writer projection manifest history[${index}] is missing`,\n );\n }\n await verifyDocumentManifestBundle({\n authorizationEvidence: input.authorizationEvidence,\n requireAuthorizationEvidence: true,\n bundle,\n bundlesByHash,\n checkpointContext: input.checkpointContext,\n containerPathByManifestHash: input.containerPathByManifestHash,\n enforceLocalCheckpoint: false,\n label: `Document writer projection manifest history[${index}]`,\n principalPolicyCache: input.principalPolicyCache,\n resolveUserKey: input.resolveUserKey,\n verifiedByHash,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n }\n\n const headManifest = await verifyDocumentManifestBundle({\n authorizationEvidence: input.authorizationEvidence,\n requireAuthorizationEvidence: true,\n bundle: input.projection.documentManifest,\n bundlesByHash,\n checkpointContext: input.checkpointContext,\n containerPathByManifestHash: input.containerPathByManifestHash,\n enforceLocalCheckpoint: true,\n label: \"Document writer projection\",\n principalPolicyCache: input.principalPolicyCache,\n resolveUserKey: input.resolveUserKey,\n verifiedByHash,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n return { headManifest, verifiedByHash };\n}\n"]}
1
+ {"version":3,"file":"documentManifestVerification.js","sourceRoot":"","sources":["documentManifestVerification.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PrincipalPolicyAuthorization } from \"@tearleads/crypto\";\nimport {\n KeyingVerificationError,\n type VerifiedContainerAccessManifest,\n type VerifiedDocumentLinkSetManifest,\n type VerifiedDocumentLinkSetSnapshot,\n verifyDocumentLinkSetManifest,\n} from \"@tearleads/crypto\";\nimport type {\n AccessManifestBundleWireResponse,\n DocumentWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { readCanonicalJson } from \"../../keying/canonicalJson\";\nimport {\n addBundleByHash,\n assertCanonicalEqual,\n verifyAccessEventBundle,\n} from \"../checkpoints/bundleVerification\";\nimport type { ProjectionCheckpointContext } from \"../checkpoints/checkpointContext\";\nimport {\n loadManifestCheckpointVerification,\n verifyCachedManifestCheckpoint,\n} from \"../checkpoints/manifestCheckpointVerification\";\nimport { recordVerifiedBundleBytes } from \"../checkpoints/verifiedBundleBytes\";\nimport { readAccessManifest, readDocumentAccessEventBody } from \"../readers\";\nimport type {\n PrincipalPolicyCache,\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../types\";\nimport { resolveEventContainerPaths } from \"./documentDependencyPaths\";\nimport {\n collectDocumentManifestPrincipalPolicies,\n recordUsedDocumentContainerManifests,\n type UsedDocumentContainerManifests,\n} from \"./documentManifestPolicies\";\nimport { requireVerifiedDocumentPredecessor } from \"./documentManifestPredecessor\";\n\ntype PolicyWarmer = ReferencedPrincipalPolicyWarmer | undefined;\n\nexport async function verifyDocumentManifestBundle(input: {\n readonly authorizationMembership?: \"current\" | \"referenced\" | undefined;\n readonly authorizationEvidence?:\n | readonly PrincipalPolicyAuthorization[]\n | undefined;\n readonly bundle: AccessManifestBundleWireResponse;\n readonly bundlesByHash: ReadonlyMap<string, AccessManifestBundleWireResponse>;\n readonly containerPathByManifestHash: ReadonlyMap<\n string,\n readonly VerifiedContainerAccessManifest[]\n >;\n readonly checkpointContext: ProjectionCheckpointContext;\n readonly enforceLocalCheckpoint: boolean;\n readonly label: string;\n readonly principalPolicyCache: PrincipalPolicyCache;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n readonly requireAuthorizationEvidence?: boolean | undefined;\n readonly trustedPredecessorByHash?:\n | ReadonlyMap<string, VerifiedDocumentLinkSetSnapshot>\n | undefined;\n readonly usedContainerManifests?: UsedDocumentContainerManifests | undefined;\n readonly verifiedByHash: Map<string, VerifiedDocumentLinkSetManifest>;\n readonly warmReferencedPrincipalPolicies?: PolicyWarmer;\n}): Promise<VerifiedDocumentLinkSetManifest> {\n const cached = input.verifiedByHash.get(input.bundle.manifestHash);\n if (cached) {\n if (input.enforceLocalCheckpoint) {\n await verifyCachedManifestCheckpoint({\n current: cached,\n execSql: input.checkpointContext.execSql,\n localCheckpoints: input.checkpointContext.localCheckpoints,\n verifiedManifests: input.verifiedByHash,\n });\n }\n return cached;\n }\n\n const event = await verifyAccessEventBundle(input);\n const manifest = readAccessManifest(\n input.bundle.manifest,\n `${input.label} manifest`,\n );\n const previousManifest = requireVerifiedDocumentPredecessor({\n label: input.label,\n previousManifestHash: event.event.previousManifestHash,\n trustedPredecessorByHash: input.trustedPredecessorByHash,\n verifiedByHash: input.verifiedByHash,\n });\n const body = readDocumentAccessEventBody(\n event.body,\n `${input.label} event body`,\n );\n const { dependencyContainerPaths, targetContainerPath } =\n resolveEventContainerPaths({\n containerPathByManifestHash: input.containerPathByManifestHash,\n dependencyManifestHashes: event.event.dependencyManifestHashes,\n targetManifestHash: body.containerManifestHash,\n });\n const principalPolicies = await collectDocumentManifestPrincipalPolicies({\n authorizationEvidence: input.authorizationEvidence,\n checkpointContext: input.checkpointContext,\n organizationId: event.event.organizationId,\n paths: [...dependencyContainerPaths, targetContainerPath],\n principalPolicyCache: input.principalPolicyCache,\n resolveUserKey: input.resolveUserKey,\n requireAuthorizationEvidence: input.requireAuthorizationEvidence,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n const checkpointVerification = input.enforceLocalCheckpoint\n ? await loadManifestCheckpointVerification({\n current: manifest,\n execSql: input.checkpointContext.execSql,\n localCheckpoints: input.checkpointContext.localCheckpoints,\n verifiedManifests: input.verifiedByHash,\n })\n : null;\n const verified = await verifyDocumentLinkSetManifest({\n // Served heads and history were committed under the group membership their\n // cited container heads referenced; a signer removed since must still\n // verify on a cold device. The API verifier gates new writes at current.\n authorizationMembership: input.authorizationMembership ?? \"referenced\",\n authorizingContainerPaths: dependencyContainerPaths,\n event,\n expectedManifestHash: input.bundle.manifestHash,\n manifest,\n previousManifest,\n principalPolicies,\n ...(checkpointVerification ?? {}),\n ...(targetContainerPath ? { targetContainerPath } : {}),\n });\n if (!verified.ok) {\n throw new KeyingVerificationError(\n verified.error.code,\n `${input.label} manifest verification failed: ${verified.error.message}`,\n );\n }\n\n recordUsedDocumentContainerManifests({\n paths: [...dependencyContainerPaths, targetContainerPath],\n used: input.usedContainerManifests,\n });\n assertCanonicalEqual({\n actual: input.bundle.state,\n expected: readCanonicalJson(verified.value.state, `${input.label} state`),\n label: `${input.label} state`,\n });\n input.verifiedByHash.set(input.bundle.manifestHash, verified.value);\n recordVerifiedBundleBytes(verified.value, input.bundle);\n\n return verified.value;\n}\n\nexport async function verifyDocumentProjectionManifests(\n input: Omit<\n Parameters<typeof verifyDocumentManifestBundle>[0],\n | \"bundle\"\n | \"bundlesByHash\"\n | \"enforceLocalCheckpoint\"\n | \"label\"\n | \"verifiedByHash\"\n > & { readonly projection: DocumentWriterProjectionResponse },\n) {\n const bundlesByHash = new Map<string, AccessManifestBundleWireResponse>();\n addBundleByHash(\n bundlesByHash,\n input.projection.documentManifest,\n \"Document writer projection manifest\",\n );\n const history = input.projection.documentManifestHistory;\n for (const [index, bundle] of history.entries()) {\n addBundleByHash(\n bundlesByHash,\n bundle,\n `Document writer projection manifest history[${index}]`,\n );\n }\n // An honest API never lists the head in its own history (it seeds the walk\n // with the head), so a repeat is a malformed or tampered projection and is\n // refused as such, rather than letting the head reach its verification\n // through the history cache and relying on the cached branch to re-run\n // every check the fresh path would.\n if (\n history.some(\n (bundle) =>\n bundle.manifestHash === input.projection.documentManifest.manifestHash,\n )\n ) {\n throw new KeyingVerificationError(\n \"duplicate_entry\",\n \"Document writer projection history repeats the current head\",\n );\n }\n\n const verifiedByHash = new Map<string, VerifiedDocumentLinkSetManifest>();\n for (let index = history.length - 1; index >= 0; index -= 1) {\n const bundle = history[index];\n if (!bundle) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n `Document writer projection manifest history[${index}] is missing`,\n );\n }\n await verifyDocumentManifestBundle({\n authorizationEvidence: input.authorizationEvidence,\n requireAuthorizationEvidence: true,\n bundle,\n bundlesByHash,\n checkpointContext: input.checkpointContext,\n containerPathByManifestHash: input.containerPathByManifestHash,\n enforceLocalCheckpoint: false,\n label: `Document writer projection manifest history[${index}]`,\n principalPolicyCache: input.principalPolicyCache,\n resolveUserKey: input.resolveUserKey,\n verifiedByHash,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n }\n\n const headManifest = await verifyDocumentManifestBundle({\n authorizationEvidence: input.authorizationEvidence,\n requireAuthorizationEvidence: true,\n bundle: input.projection.documentManifest,\n bundlesByHash,\n checkpointContext: input.checkpointContext,\n containerPathByManifestHash: input.containerPathByManifestHash,\n enforceLocalCheckpoint: true,\n label: \"Document writer projection\",\n principalPolicyCache: input.principalPolicyCache,\n resolveUserKey: input.resolveUserKey,\n verifiedByHash,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n return { headManifest, verifiedByHash };\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { eq } from "drizzle-orm";
2
- import { containerHydrationTombstones, containers, documentContainerProjectionTables, localIntentTables, } from "../../sqlite/schema/index.js";
2
+ import { containerHydrationTombstones, containers, documentContainerProjectionTables, keyingCheckpointTables, localIntentTables, } from "../../sqlite/schema/index.js";
3
3
  import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
4
4
  import { ensureSqlTables, runOncePerConnection, runSerializedSqlMutation, } from "../../sqlite/sqlSchema.js";
5
5
  import { enqueueDocumentPendingUpdate, ensureDocumentProjectionTables, ensureDocumentTables, } from "../documents/records/index.js";
@@ -90,6 +90,8 @@ export const sqlContainerContentsPersistence = {
90
90
  await ensureContainerTables(lockedExecSql);
91
91
  await ensureDocumentTables(lockedExecSql);
92
92
  await ensureSqlTables(lockedExecSql, localIntentTables);
93
+ // Metadata rows consult document purge checkpoints before writing.
94
+ await ensureSqlTables(lockedExecSql, keyingCheckpointTables);
93
95
  await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);
94
96
  await ensureDocumentProjectionTables(lockedExecSql);
95
97
  await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);
@@ -1 +1 @@
1
- {"version":3,"file":"containerContentsPersistence.js","sourceRoot":"","sources":["containerContentsPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { eq } from \"drizzle-orm\";\nimport {\n containerHydrationTombstones,\n containers,\n documentContainerProjectionTables,\n localIntentTables,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n ensureSqlTables,\n runOncePerConnection,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n enqueueDocumentPendingUpdate,\n ensureDocumentProjectionTables,\n ensureDocumentTables,\n} from \"../documents/records\";\nimport {\n hasQueuedDocumentUpdates,\n settleQueuedDocumentUpdatesInTransaction,\n} from \"../documents/records/documentPendingUpdatePersistence\";\nimport { getLatestTimestamp } from \"../latestTimestamp\";\nimport {\n ensureContainerTables,\n loadContainerById,\n loadContainers as loadContainerRecords,\n} from \"./containerPersistence\";\nimport { sqlContainerSyncWatermarkPersistence } from \"./containerSyncWatermarkPersistence\";\nimport {\n claimDormantMetadataSweepAttempt,\n completeDormantMetadataSweepRequest,\n listDormantMetadataSweepRequests,\n} from \"./dormantMetadataSweep\";\n\nexport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerHydrationTombstone,\n ContainerMetadataRecord,\n ContainerMoveIntentRecord,\n LocalRootDescendantReparentInput,\n StoredContainerState,\n} from \"./containerContentsPersistenceTypes\";\nexport { CONTAINER_METADATA_APP_KIND } from \"./dormantContainerMetadata\";\n\nimport type {\n ContainerContentsPersistence,\n ContainerMetadataRecord,\n SaveContainerOptions,\n SaveContainerWithPendingUpdateOptions,\n} from \"./containerContentsPersistenceTypes\";\nimport { deleteStoredContainers } from \"./containerDeletionPersistence\";\nimport {\n commitStoredHydratedContainer,\n loadStoredHeldContainerBinding,\n} from \"./containerHydrationPersistence\";\nimport { containerIntentPersistence } from \"./containerIntentPersistence\";\nimport {\n commitStoredMetadataMutation,\n settleStoredMetadataPendingUpdates,\n} from \"./containerMetadataMutationPersistence\";\nimport { containerMetadataPullContinuationPersistence } from \"./containerMetadataPullContinuationPersistence\";\nimport {\n getContainerMetadataScope,\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n} from \"./containerMetadataRows\";\nimport { abandonStoredMoveIntentRevision } from \"./containerMoveAbandonPersistence\";\nimport { containerPendingUpdatePersistence } from \"./containerPendingUpdatePersistence\";\nimport { rebindStoredHeldContainer } from \"./containerRebindPersistence\";\nimport { containerReconcilePersistence } from \"./containerReconcilePersistence\";\nimport { isSupersededContainerBinding } from \"./supersededContainerBindings\";\n\nasync function saveStoredContainer(\n execSql: Parameters<ContainerContentsPersistence[\"saveContainer\"]>[0],\n container: Parameters<ContainerContentsPersistence[\"saveContainer\"]>[1],\n record: ContainerMetadataRecord | null,\n options?:\n | SaveContainerOptions\n | SaveContainerWithPendingUpdateOptions\n | undefined,\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const localUpdatedAt =\n options?.localUpdatedAt ?? options?.updatedAt ?? new Date().toISOString();\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const save = async (tx: ClientSQLiteTransactionScope) => {\n const saved = await saveContainerContentsContainerRows({\n container,\n createIntent: options?.createIntent,\n moveIntent: options?.moveIntent,\n record,\n serverTimestamps: options?.serverTimestamps,\n tx,\n localUpdatedAt,\n });\n if (options && \"pendingUpdate\" in options) {\n await enqueueDocumentPendingUpdate(\n lockedExecSql,\n getContainerMetadataScope(container.id),\n options.pendingUpdate,\n );\n }\n return saved;\n };\n if (!options?.stillCurrent) {\n return runtime.transaction(save);\n }\n const outcome = await runtime.guardedTransaction(\n save,\n options.stillCurrent,\n { behavior: \"immediate\" },\n );\n // Guarded callers recheck the same monotonic generation immediately after\n // this await. Returning the untouched candidate keeps the long-standing\n // non-null save contract while the transaction itself remains rolled back.\n return outcome.result ?? container;\n });\n}\n\nexport const sqlContainerContentsPersistence: ContainerContentsPersistence = {\n ...containerIntentPersistence,\n ...containerMetadataPullContinuationPersistence,\n commitHydratedContainer: commitStoredHydratedContainer,\n commitMetadataMutation: commitStoredMetadataMutation,\n settleAcceptedMetadataPendingUpdates: settleStoredMetadataPendingUpdates,\n listDormantMetadataSweepRequests,\n ...containerPendingUpdatePersistence,\n ...containerReconcilePersistence,\n claimDormantMetadataSweepAttempt,\n completeDormantMetadataSweepRequest,\n async containerExists(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ id: containers.id })\n .from(containers)\n .where(eq(containers.id, containerId))\n .limit(1);\n return rows.length > 0;\n },\n async deleteContainer(execSql, containerId, options) {\n await sqlContainerContentsPersistence.deleteContainers(execSql, [\n {\n containerId,\n reason: options?.reason ?? \"deleted\",\n updatedAt: options?.updatedAt ?? new Date().toISOString(),\n },\n ]);\n },\n async deleteContainers(execSql, removals, options) {\n return deleteStoredContainers(execSql, removals, options);\n },\n async ensureSchema(execSql) {\n // Once ensured on this connection, skip the outer mutation lock entirely:\n // ensureSchema runs on every query path, and re-acquiring the lock just to\n // no-op would queue reads behind unrelated writes.\n await runOncePerConnection(execSql, \"ensure:container-contents\", () =>\n runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureContainerTables(lockedExecSql);\n await ensureDocumentTables(lockedExecSql);\n await ensureSqlTables(lockedExecSql, localIntentTables);\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n await ensureDocumentProjectionTables(lockedExecSql);\n await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);\n }),\n );\n },\n async loadHeldContainerBinding(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return loadStoredHeldContainerBinding(execSql, containerId);\n },\n async isSupersededContainerBinding(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return isSupersededContainerBinding(execSql, input);\n },\n async abandonMoveIntentRevision(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return abandonStoredMoveIntentRevision(execSql, input);\n },\n async rebindHeldContainer(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return rebindStoredHeldContainer(execSql, input);\n },\n async loadContainerMetadataRecord(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return selectContainerMetadataRecord(execSql, containerId);\n },\n async loadContainerHydrationTombstones(execSql) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n containerId: containerHydrationTombstones.containerId,\n generation: containerHydrationTombstones.generation,\n reason: containerHydrationTombstones.reason,\n updatedAt: containerHydrationTombstones.updatedAt,\n })\n .from(containerHydrationTombstones)\n .where(eq(containerHydrationTombstones.cleared, false));\n return rows.flatMap((row) =>\n row.reason === \"access_revoked\" || row.reason === \"deleted\"\n ? [{ ...row, reason: row.reason }]\n : [],\n );\n },\n async loadContainers(execSql) {\n const containers = await loadContainerRecords(execSql);\n const storedContainers = await Promise.all(\n containers.map(async (container) => ({\n container,\n record: await selectContainerMetadataRecord(execSql, container.id),\n })),\n );\n\n return storedContainers;\n },\n async loadContainerMetadataState(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const container = await loadContainerById(execSql, containerId);\n if (!container) {\n return null;\n }\n return {\n container,\n record: await selectContainerMetadataRecord(execSql, containerId),\n };\n },\n async saveContainer(execSql, container, record, options) {\n return saveStoredContainer(execSql, container, record, options);\n },\n async saveContainerWithPendingUpdate(execSql, container, record, options) {\n return saveStoredContainer(execSql, container, record, options);\n },\n async saveContainerAndDeletePendingUpdates(\n execSql,\n container,\n record,\n pendingUpdateIds,\n ) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const localUpdatedAt = new Date().toISOString();\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const scope = getContainerMetadataScope(container.id);\n await settleQueuedDocumentUpdatesInTransaction(\n tx,\n scope,\n pendingUpdateIds,\n );\n const hasRemainingPendingUpdates = await hasQueuedDocumentUpdates(\n tx,\n scope,\n );\n const containerToSave = hasRemainingPendingUpdates\n ? container\n : {\n ...container,\n serverUpdatedAt: getLatestTimestamp(\n container.serverUpdatedAt,\n localUpdatedAt,\n ),\n };\n\n return saveContainerContentsContainerRows({\n container: containerToSave,\n record,\n tx,\n localUpdatedAt,\n });\n },\n );\n });\n },\n};\n"]}
1
+ {"version":3,"file":"containerContentsPersistence.js","sourceRoot":"","sources":["containerContentsPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { eq } from \"drizzle-orm\";\nimport {\n containerHydrationTombstones,\n containers,\n documentContainerProjectionTables,\n keyingCheckpointTables,\n localIntentTables,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n ensureSqlTables,\n runOncePerConnection,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n enqueueDocumentPendingUpdate,\n ensureDocumentProjectionTables,\n ensureDocumentTables,\n} from \"../documents/records\";\nimport {\n hasQueuedDocumentUpdates,\n settleQueuedDocumentUpdatesInTransaction,\n} from \"../documents/records/documentPendingUpdatePersistence\";\nimport { getLatestTimestamp } from \"../latestTimestamp\";\nimport {\n ensureContainerTables,\n loadContainerById,\n loadContainers as loadContainerRecords,\n} from \"./containerPersistence\";\nimport { sqlContainerSyncWatermarkPersistence } from \"./containerSyncWatermarkPersistence\";\nimport {\n claimDormantMetadataSweepAttempt,\n completeDormantMetadataSweepRequest,\n listDormantMetadataSweepRequests,\n} from \"./dormantMetadataSweep\";\n\nexport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerHydrationTombstone,\n ContainerMetadataRecord,\n ContainerMoveIntentRecord,\n LocalRootDescendantReparentInput,\n StoredContainerState,\n} from \"./containerContentsPersistenceTypes\";\nexport { CONTAINER_METADATA_APP_KIND } from \"./dormantContainerMetadata\";\n\nimport type {\n ContainerContentsPersistence,\n ContainerMetadataRecord,\n SaveContainerOptions,\n SaveContainerWithPendingUpdateOptions,\n} from \"./containerContentsPersistenceTypes\";\nimport { deleteStoredContainers } from \"./containerDeletionPersistence\";\nimport {\n commitStoredHydratedContainer,\n loadStoredHeldContainerBinding,\n} from \"./containerHydrationPersistence\";\nimport { containerIntentPersistence } from \"./containerIntentPersistence\";\nimport {\n commitStoredMetadataMutation,\n settleStoredMetadataPendingUpdates,\n} from \"./containerMetadataMutationPersistence\";\nimport { containerMetadataPullContinuationPersistence } from \"./containerMetadataPullContinuationPersistence\";\nimport {\n getContainerMetadataScope,\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n} from \"./containerMetadataRows\";\nimport { abandonStoredMoveIntentRevision } from \"./containerMoveAbandonPersistence\";\nimport { containerPendingUpdatePersistence } from \"./containerPendingUpdatePersistence\";\nimport { rebindStoredHeldContainer } from \"./containerRebindPersistence\";\nimport { containerReconcilePersistence } from \"./containerReconcilePersistence\";\nimport { isSupersededContainerBinding } from \"./supersededContainerBindings\";\n\nasync function saveStoredContainer(\n execSql: Parameters<ContainerContentsPersistence[\"saveContainer\"]>[0],\n container: Parameters<ContainerContentsPersistence[\"saveContainer\"]>[1],\n record: ContainerMetadataRecord | null,\n options?:\n | SaveContainerOptions\n | SaveContainerWithPendingUpdateOptions\n | undefined,\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const localUpdatedAt =\n options?.localUpdatedAt ?? options?.updatedAt ?? new Date().toISOString();\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const save = async (tx: ClientSQLiteTransactionScope) => {\n const saved = await saveContainerContentsContainerRows({\n container,\n createIntent: options?.createIntent,\n moveIntent: options?.moveIntent,\n record,\n serverTimestamps: options?.serverTimestamps,\n tx,\n localUpdatedAt,\n });\n if (options && \"pendingUpdate\" in options) {\n await enqueueDocumentPendingUpdate(\n lockedExecSql,\n getContainerMetadataScope(container.id),\n options.pendingUpdate,\n );\n }\n return saved;\n };\n if (!options?.stillCurrent) {\n return runtime.transaction(save);\n }\n const outcome = await runtime.guardedTransaction(\n save,\n options.stillCurrent,\n { behavior: \"immediate\" },\n );\n // Guarded callers recheck the same monotonic generation immediately after\n // this await. Returning the untouched candidate keeps the long-standing\n // non-null save contract while the transaction itself remains rolled back.\n return outcome.result ?? container;\n });\n}\n\nexport const sqlContainerContentsPersistence: ContainerContentsPersistence = {\n ...containerIntentPersistence,\n ...containerMetadataPullContinuationPersistence,\n commitHydratedContainer: commitStoredHydratedContainer,\n commitMetadataMutation: commitStoredMetadataMutation,\n settleAcceptedMetadataPendingUpdates: settleStoredMetadataPendingUpdates,\n listDormantMetadataSweepRequests,\n ...containerPendingUpdatePersistence,\n ...containerReconcilePersistence,\n claimDormantMetadataSweepAttempt,\n completeDormantMetadataSweepRequest,\n async containerExists(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ id: containers.id })\n .from(containers)\n .where(eq(containers.id, containerId))\n .limit(1);\n return rows.length > 0;\n },\n async deleteContainer(execSql, containerId, options) {\n await sqlContainerContentsPersistence.deleteContainers(execSql, [\n {\n containerId,\n reason: options?.reason ?? \"deleted\",\n updatedAt: options?.updatedAt ?? new Date().toISOString(),\n },\n ]);\n },\n async deleteContainers(execSql, removals, options) {\n return deleteStoredContainers(execSql, removals, options);\n },\n async ensureSchema(execSql) {\n // Once ensured on this connection, skip the outer mutation lock entirely:\n // ensureSchema runs on every query path, and re-acquiring the lock just to\n // no-op would queue reads behind unrelated writes.\n await runOncePerConnection(execSql, \"ensure:container-contents\", () =>\n runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureContainerTables(lockedExecSql);\n await ensureDocumentTables(lockedExecSql);\n await ensureSqlTables(lockedExecSql, localIntentTables);\n // Metadata rows consult document purge checkpoints before writing.\n await ensureSqlTables(lockedExecSql, keyingCheckpointTables);\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n await ensureDocumentProjectionTables(lockedExecSql);\n await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);\n }),\n );\n },\n async loadHeldContainerBinding(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return loadStoredHeldContainerBinding(execSql, containerId);\n },\n async isSupersededContainerBinding(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return isSupersededContainerBinding(execSql, input);\n },\n async abandonMoveIntentRevision(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return abandonStoredMoveIntentRevision(execSql, input);\n },\n async rebindHeldContainer(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return rebindStoredHeldContainer(execSql, input);\n },\n async loadContainerMetadataRecord(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return selectContainerMetadataRecord(execSql, containerId);\n },\n async loadContainerHydrationTombstones(execSql) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n containerId: containerHydrationTombstones.containerId,\n generation: containerHydrationTombstones.generation,\n reason: containerHydrationTombstones.reason,\n updatedAt: containerHydrationTombstones.updatedAt,\n })\n .from(containerHydrationTombstones)\n .where(eq(containerHydrationTombstones.cleared, false));\n return rows.flatMap((row) =>\n row.reason === \"access_revoked\" || row.reason === \"deleted\"\n ? [{ ...row, reason: row.reason }]\n : [],\n );\n },\n async loadContainers(execSql) {\n const containers = await loadContainerRecords(execSql);\n const storedContainers = await Promise.all(\n containers.map(async (container) => ({\n container,\n record: await selectContainerMetadataRecord(execSql, container.id),\n })),\n );\n\n return storedContainers;\n },\n async loadContainerMetadataState(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const container = await loadContainerById(execSql, containerId);\n if (!container) {\n return null;\n }\n return {\n container,\n record: await selectContainerMetadataRecord(execSql, containerId),\n };\n },\n async saveContainer(execSql, container, record, options) {\n return saveStoredContainer(execSql, container, record, options);\n },\n async saveContainerWithPendingUpdate(execSql, container, record, options) {\n return saveStoredContainer(execSql, container, record, options);\n },\n async saveContainerAndDeletePendingUpdates(\n execSql,\n container,\n record,\n pendingUpdateIds,\n ) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const localUpdatedAt = new Date().toISOString();\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const scope = getContainerMetadataScope(container.id);\n await settleQueuedDocumentUpdatesInTransaction(\n tx,\n scope,\n pendingUpdateIds,\n );\n const hasRemainingPendingUpdates = await hasQueuedDocumentUpdates(\n tx,\n scope,\n );\n const containerToSave = hasRemainingPendingUpdates\n ? container\n : {\n ...container,\n serverUpdatedAt: getLatestTimestamp(\n container.serverUpdatedAt,\n localUpdatedAt,\n ),\n };\n\n return saveContainerContentsContainerRows({\n container: containerToSave,\n record,\n tx,\n localUpdatedAt,\n });\n },\n );\n });\n },\n};\n"]}
@@ -6,6 +6,7 @@ import { documentHistoryCheckpoints, documents } from "../../sqlite/schema/index
6
6
  import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
7
7
  import { documentRecordSelection, mapSelectedDocumentRecord, } from "../documents/records/index.js";
8
8
  import { upsertDocumentHistoryCheckpointInTransaction, upsertDocumentRowInTransaction, } from "../documents/records/documentRowsInTransaction.js";
9
+ import { hasDocumentPurgeCheckpointInTransaction } from "../keyingCheckpoints/documentPurgeCheckpointPersistence.js";
9
10
  import { saveContainerCreateIntent } from "./containerIntentPersistence.js";
10
11
  import { saveContainerMoveIntent } from "./containerMoveIntentRows.js";
11
12
  import { saveContainerRows, } from "./containerPersistence.js";
@@ -32,6 +33,14 @@ function deriveMetadataEndVersion(metadataUpdates) {
32
33
  }
33
34
  async function saveContainerMetadataRecord(input) {
34
35
  const { containerId, record, tx, updatedAt } = input;
36
+ // The API refuses a container whose metadata id was permanently purged, so
37
+ // a served head naming one predates that rule or is dishonest. Keep the
38
+ // purged id's row from returning, but still store the container itself:
39
+ // its metadata is unavailable either way, and refusing would stall hydration.
40
+ if (record.documentId !== null &&
41
+ (await hasDocumentPurgeCheckpointInTransaction(tx, record.documentId))) {
42
+ return;
43
+ }
35
44
  const snapshotEndVersion = deriveMetadataEndVersion(record.metadataUpdates);
36
45
  const nextRow = {
37
46
  appKind: CONTAINER_METADATA_APP_KIND,
@@ -1 +1 @@
1
- {"version":3,"file":"containerMetadataRows.js","sourceRoot":"","sources":["containerMetadataRows.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64ToBytes } from \"@tearleads/encoding\";\nimport { getImportBlobMetadata } from \"@tearleads/loro\";\nimport { and, eq } from \"drizzle-orm\";\nimport { serializeDocumentSyncPullContinuation } from \"../../documents/shared/sync/pullContinuation\";\nimport { documentHistoryCheckpoints, documents } from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport {\n documentRecordSelection,\n mapSelectedDocumentRecord,\n} from \"../documents/records\";\nimport {\n upsertDocumentHistoryCheckpointInTransaction,\n upsertDocumentRowInTransaction,\n} from \"../documents/records/documentRowsInTransaction\";\nimport type {\n ContainerCreateIntentInput,\n ContainerMetadataRecord,\n ContainerMoveIntentInput,\n} from \"./containerContentsPersistenceTypes\";\nimport { saveContainerCreateIntent } from \"./containerIntentPersistence\";\nimport { saveContainerMoveIntent } from \"./containerMoveIntentRows\";\nimport {\n type ContainerRecord,\n saveContainerRows,\n} from \"./containerPersistence\";\nimport {\n CONTAINER_METADATA_APP_KIND,\n clearDormantContainerMetadataInTransaction,\n} from \"./dormantContainerMetadata\";\n\nexport function getContainerMetadataScope(containerId: string) {\n return {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n };\n}\n\n// The metadata document's content frontier, derived from its full updates\n// export. Empty or undecodable blobs read as \"never hydrated\".\nfunction deriveMetadataEndVersion(metadataUpdates: string): string {\n if (metadataUpdates.length === 0) {\n return \"\";\n }\n\n try {\n return getImportBlobMetadata(base64ToBytes(metadataUpdates))\n .partialEndVersionVector;\n } catch {\n return \"\";\n }\n}\n\nasync function saveContainerMetadataRecord(input: {\n containerId: string;\n record: ContainerMetadataRecord;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}) {\n const { containerId, record, tx, updatedAt } = input;\n const snapshotEndVersion = deriveMetadataEndVersion(record.metadataUpdates);\n const nextRow = {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n documentId: record.documentId,\n snapshotEndVersion,\n accessEpoch: record.accessEpoch,\n accessStateHash: record.accessStateHash ?? null,\n lastCommitLsn: record.lastCommitLsn ?? null,\n documentManifestBundle: record.documentManifestBundle ?? null,\n contentKeyBundle: record.contentKeyBundle ?? null,\n documentKekTargets: record.documentKekTargets ?? null,\n ...(record.pullContinuationRecoveryRequired === true &&\n record.pullContinuation === undefined\n ? {}\n : {\n pullContinuation: serializeDocumentSyncPullContinuation(\n record.pullContinuation ?? null,\n ),\n }),\n updatedAt,\n };\n\n await upsertDocumentRowInTransaction(tx, nextRow);\n\n // The metadata document's content lives in the shared history-checkpoint\n // table, like every other document kind. The whole updates export IS the\n // checkpoint: metadata documents are tiny registries, so no tail/compaction\n // machinery is needed, and the row replaces atomically with the record.\n const checkpointRow = {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n snapshot: record.metadataUpdates,\n endVersionVector: snapshotEndVersion,\n revision: updatedAt,\n updatedAt,\n };\n await upsertDocumentHistoryCheckpointInTransaction(tx, checkpointRow);\n}\n\nexport async function saveContainerContentsContainerRows(input: {\n container: ContainerRecord;\n createIntent?: ContainerCreateIntentInput | undefined;\n moveIntent?: ContainerMoveIntentInput | undefined;\n record: ContainerMetadataRecord | null;\n tx: ClientSQLiteTransactionScope;\n localUpdatedAt: string;\n serverTimestamps?:\n | {\n createdAt?: string | null;\n updatedAt?: string | null;\n }\n | undefined;\n}): Promise<ContainerRecord> {\n const {\n container,\n createIntent,\n moveIntent,\n localUpdatedAt,\n record,\n serverTimestamps,\n tx,\n } = input;\n const nextContainer = {\n ...container,\n ...(serverTimestamps\n ? {\n serverCreatedAt:\n serverTimestamps.createdAt ?? container.serverCreatedAt ?? null,\n serverUpdatedAt:\n serverTimestamps.updatedAt ?? container.serverUpdatedAt ?? null,\n }\n : {}),\n };\n\n const savedContainer = await saveContainerRows({\n record: nextContainer,\n tx,\n localUpdatedAt,\n });\n await clearDormantContainerMetadataInTransaction(tx, [container.id]);\n\n if (record) {\n await saveContainerMetadataRecord({\n containerId: container.id,\n record: {\n ...record,\n id: container.id,\n },\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n if (createIntent) {\n await saveContainerCreateIntent({\n containerId: container.id,\n createIntent,\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n if (moveIntent) {\n await saveContainerMoveIntent({\n containerId: container.id,\n moveIntent,\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n return savedContainer;\n}\n\n/**\n * ONE joined statement, so the record's key/access fields and the checkpoint\n * content it pairs with come from a single consistent read — two separate\n * queries could interleave with a concurrent metadata save (record +\n * checkpoint written in one transaction) and pair stale keying state with\n * newer content.\n */\nexport async function selectContainerMetadataRecord(\n execSql: ExecSql,\n containerId: string,\n): Promise<ContainerMetadataRecord | null> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n ...documentRecordSelection,\n metadataUpdates: documentHistoryCheckpoints.snapshot,\n })\n .from(documents)\n .leftJoin(\n documentHistoryCheckpoints,\n and(\n eq(documentHistoryCheckpoints.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documentHistoryCheckpoints.localId, documents.localId),\n ),\n )\n .where(\n and(\n eq(documents.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documents.localId, containerId),\n ),\n )\n .limit(1);\n const row = rows[0];\n if (!row) {\n return null;\n }\n const { metadataUpdates, ...recordRow } = row;\n return {\n ...mapSelectedDocumentRecord(recordRow),\n metadataUpdates: metadataUpdates ?? \"\",\n };\n}\n"]}
1
+ {"version":3,"file":"containerMetadataRows.js","sourceRoot":"","sources":["containerMetadataRows.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64ToBytes } from \"@tearleads/encoding\";\nimport { getImportBlobMetadata } from \"@tearleads/loro\";\nimport { and, eq } from \"drizzle-orm\";\nimport { serializeDocumentSyncPullContinuation } from \"../../documents/shared/sync/pullContinuation\";\nimport { documentHistoryCheckpoints, documents } from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport {\n documentRecordSelection,\n mapSelectedDocumentRecord,\n} from \"../documents/records\";\nimport {\n upsertDocumentHistoryCheckpointInTransaction,\n upsertDocumentRowInTransaction,\n} from \"../documents/records/documentRowsInTransaction\";\nimport { hasDocumentPurgeCheckpointInTransaction } from \"../keyingCheckpoints/documentPurgeCheckpointPersistence\";\nimport type {\n ContainerCreateIntentInput,\n ContainerMetadataRecord,\n ContainerMoveIntentInput,\n} from \"./containerContentsPersistenceTypes\";\nimport { saveContainerCreateIntent } from \"./containerIntentPersistence\";\nimport { saveContainerMoveIntent } from \"./containerMoveIntentRows\";\nimport {\n type ContainerRecord,\n saveContainerRows,\n} from \"./containerPersistence\";\nimport {\n CONTAINER_METADATA_APP_KIND,\n clearDormantContainerMetadataInTransaction,\n} from \"./dormantContainerMetadata\";\n\nexport function getContainerMetadataScope(containerId: string) {\n return {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n };\n}\n\n// The metadata document's content frontier, derived from its full updates\n// export. Empty or undecodable blobs read as \"never hydrated\".\nfunction deriveMetadataEndVersion(metadataUpdates: string): string {\n if (metadataUpdates.length === 0) {\n return \"\";\n }\n\n try {\n return getImportBlobMetadata(base64ToBytes(metadataUpdates))\n .partialEndVersionVector;\n } catch {\n return \"\";\n }\n}\n\nasync function saveContainerMetadataRecord(input: {\n containerId: string;\n record: ContainerMetadataRecord;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}) {\n const { containerId, record, tx, updatedAt } = input;\n // The API refuses a container whose metadata id was permanently purged, so\n // a served head naming one predates that rule or is dishonest. Keep the\n // purged id's row from returning, but still store the container itself:\n // its metadata is unavailable either way, and refusing would stall hydration.\n if (\n record.documentId !== null &&\n (await hasDocumentPurgeCheckpointInTransaction(tx, record.documentId))\n ) {\n return;\n }\n const snapshotEndVersion = deriveMetadataEndVersion(record.metadataUpdates);\n const nextRow = {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n documentId: record.documentId,\n snapshotEndVersion,\n accessEpoch: record.accessEpoch,\n accessStateHash: record.accessStateHash ?? null,\n lastCommitLsn: record.lastCommitLsn ?? null,\n documentManifestBundle: record.documentManifestBundle ?? null,\n contentKeyBundle: record.contentKeyBundle ?? null,\n documentKekTargets: record.documentKekTargets ?? null,\n ...(record.pullContinuationRecoveryRequired === true &&\n record.pullContinuation === undefined\n ? {}\n : {\n pullContinuation: serializeDocumentSyncPullContinuation(\n record.pullContinuation ?? null,\n ),\n }),\n updatedAt,\n };\n\n await upsertDocumentRowInTransaction(tx, nextRow);\n\n // The metadata document's content lives in the shared history-checkpoint\n // table, like every other document kind. The whole updates export IS the\n // checkpoint: metadata documents are tiny registries, so no tail/compaction\n // machinery is needed, and the row replaces atomically with the record.\n const checkpointRow = {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n snapshot: record.metadataUpdates,\n endVersionVector: snapshotEndVersion,\n revision: updatedAt,\n updatedAt,\n };\n await upsertDocumentHistoryCheckpointInTransaction(tx, checkpointRow);\n}\n\nexport async function saveContainerContentsContainerRows(input: {\n container: ContainerRecord;\n createIntent?: ContainerCreateIntentInput | undefined;\n moveIntent?: ContainerMoveIntentInput | undefined;\n record: ContainerMetadataRecord | null;\n tx: ClientSQLiteTransactionScope;\n localUpdatedAt: string;\n serverTimestamps?:\n | {\n createdAt?: string | null;\n updatedAt?: string | null;\n }\n | undefined;\n}): Promise<ContainerRecord> {\n const {\n container,\n createIntent,\n moveIntent,\n localUpdatedAt,\n record,\n serverTimestamps,\n tx,\n } = input;\n const nextContainer = {\n ...container,\n ...(serverTimestamps\n ? {\n serverCreatedAt:\n serverTimestamps.createdAt ?? container.serverCreatedAt ?? null,\n serverUpdatedAt:\n serverTimestamps.updatedAt ?? container.serverUpdatedAt ?? null,\n }\n : {}),\n };\n\n const savedContainer = await saveContainerRows({\n record: nextContainer,\n tx,\n localUpdatedAt,\n });\n await clearDormantContainerMetadataInTransaction(tx, [container.id]);\n\n if (record) {\n await saveContainerMetadataRecord({\n containerId: container.id,\n record: {\n ...record,\n id: container.id,\n },\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n if (createIntent) {\n await saveContainerCreateIntent({\n containerId: container.id,\n createIntent,\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n if (moveIntent) {\n await saveContainerMoveIntent({\n containerId: container.id,\n moveIntent,\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n return savedContainer;\n}\n\n/**\n * ONE joined statement, so the record's key/access fields and the checkpoint\n * content it pairs with come from a single consistent read — two separate\n * queries could interleave with a concurrent metadata save (record +\n * checkpoint written in one transaction) and pair stale keying state with\n * newer content.\n */\nexport async function selectContainerMetadataRecord(\n execSql: ExecSql,\n containerId: string,\n): Promise<ContainerMetadataRecord | null> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n ...documentRecordSelection,\n metadataUpdates: documentHistoryCheckpoints.snapshot,\n })\n .from(documents)\n .leftJoin(\n documentHistoryCheckpoints,\n and(\n eq(documentHistoryCheckpoints.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documentHistoryCheckpoints.localId, documents.localId),\n ),\n )\n .where(\n and(\n eq(documents.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documents.localId, containerId),\n ),\n )\n .limit(1);\n const row = rows[0];\n if (!row) {\n return null;\n }\n const { metadataUpdates, ...recordRow } = row;\n return {\n ...mapSelectedDocumentRecord(recordRow),\n metadataUpdates: metadataUpdates ?? \"\",\n };\n}\n"]}
@@ -78,4 +78,54 @@ export async function verifyPrincipalPolicyBundleWithExternalOrganizationAdmins(
78
78
  });
79
79
  return verifiedWithExternalAdmins;
80
80
  }
81
+ /**
82
+ * Whether served Admins evidence could make this bundle verify. It re-runs
83
+ * the verifier with an optimistic authority in which every Admins head the
84
+ * chain cites lists its citing signer as an admin. A bundle that still fails
85
+ * cannot be authorized by any evidence (for example a non-admin signer with
86
+ * no external citation), so its refusal is a real verification failure
87
+ * rather than missing evidence. The optimistic authority is never trusted
88
+ * for anything else: the caller caches nothing either way.
89
+ */
90
+ export async function externalAuthorityCouldAuthorize(input) {
91
+ const states = [
92
+ input.bundle.currentState,
93
+ ...input.bundle.previousStates.map((entry) => entry.state),
94
+ ];
95
+ const optimistic = new Map();
96
+ for (const state of states) {
97
+ const head = state.externalAuthority;
98
+ if (!head)
99
+ continue;
100
+ const key = JSON.stringify([
101
+ head.principalType,
102
+ head.principalId,
103
+ head.version,
104
+ head.keyEpoch,
105
+ head.stateHash,
106
+ head.keyFingerprint,
107
+ ]);
108
+ const projection = optimistic.get(key)?.projection ?? [];
109
+ optimistic.set(key, {
110
+ head,
111
+ projection: projection.some((member) => member.userId === state.signerUserId)
112
+ ? projection
113
+ : [...projection, { userId: state.signerUserId, role: "admin" }],
114
+ });
115
+ }
116
+ const [first] = optimistic.values();
117
+ if (!first)
118
+ return false;
119
+ const verified = await verifyPrincipalPolicyBundle({
120
+ bundle: input.bundle,
121
+ expectedReference: input.expectedReference,
122
+ externalAuthority: {
123
+ currentHead: first.head,
124
+ states: [...optimistic.values()],
125
+ },
126
+ localCheckpoint: input.localCheckpoint ?? null,
127
+ signerPublicKeys: input.signerPublicKeys,
128
+ });
129
+ return verified.ok;
130
+ }
81
131
  //# sourceMappingURL=principalPolicyAdminSigners.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"principalPolicyAdminSigners.js","sourceRoot":"","sources":["principalPolicyAdminSigners.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type KeyingVerificationResult,\n type PrincipalPolicyCheckpoint,\n type PrincipalPolicyExternalAuthority,\n type PrincipalPolicySignerPublicKey,\n type ReferencedPrincipalHead,\n type VerifiedPrincipalPolicy,\n verifyPrincipalPolicyBundle,\n} from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\n\nexport function principalPolicyReferenceFromBundle(\n bundle: Pick<PrincipalPolicyBundleResponse, \"currentState\">,\n): ReferencedPrincipalHead {\n return {\n principalType: bundle.currentState.principalType,\n principalId: bundle.currentState.principalId,\n version: bundle.currentState.version,\n keyEpoch: bundle.currentState.keyEpoch,\n stateHash: bundle.currentState.stateHash,\n keyFingerprint: bundle.currentState.keyFingerprint,\n };\n}\n\nexport function organizationAdminSignerUserIds(\n policy: Pick<VerifiedPrincipalPolicy, \"projection\">,\n): string[] {\n return [\n ...new Set(\n policy.projection\n .filter((member) => member.role === \"admin\")\n .map((member) => member.userId),\n ),\n ].sort();\n}\n\nexport function organizationAdminExternalAuthority(\n policy: VerifiedPrincipalPolicy,\n): PrincipalPolicyExternalAuthority {\n const history = policy.history ?? [\n {\n state: policy.state,\n projection: policy.projection,\n grants: policy.grants,\n },\n ];\n const toHead = (state: (typeof history)[number][\"state\"]) => ({\n principalType: \"group\" as const,\n principalId: state.principalId,\n version: state.version,\n keyEpoch: state.keyEpoch,\n stateHash: state.stateHash,\n keyFingerprint: state.keyFingerprint,\n });\n return {\n currentHead: toHead(policy.state),\n states: history.map((entry) => ({\n head: toHead(entry.state),\n projection: entry.projection,\n })),\n };\n}\n\nexport async function verifyOrganizationAdminPolicy(input: {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly localCheckpoint?: PrincipalPolicyCheckpoint | null | undefined;\n readonly organizationId: string;\n readonly signerPublicKeys: readonly PrincipalPolicySignerPublicKey[];\n}): Promise<KeyingVerificationResult<VerifiedPrincipalPolicy>> {\n if (\n input.bundle.currentState.principalType !== \"organization\" ||\n input.bundle.currentState.principalId !== input.organizationId\n ) {\n return {\n ok: false,\n error: new KeyingVerificationError(\n \"object_mismatch\",\n \"organization policy does not match the expected organization\",\n ),\n };\n }\n\n return verifyPrincipalPolicyBundle({\n bundle: input.bundle,\n expectedReference: principalPolicyReferenceFromBundle(input.bundle),\n localCheckpoint: input.localCheckpoint ?? null,\n signerPublicKeys: input.signerPublicKeys,\n });\n}\n\nexport async function verifyPrincipalPolicyBundleWithExternalOrganizationAdmins(input: {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly expectedReference: ReferencedPrincipalHead;\n readonly loadExternalAuthority: () => Promise<PrincipalPolicyExternalAuthority | null>;\n readonly localCheckpoint?: PrincipalPolicyCheckpoint | null | undefined;\n readonly signerPublicKeys: readonly PrincipalPolicySignerPublicKey[];\n}): Promise<KeyingVerificationResult<VerifiedPrincipalPolicy>> {\n const verified = await verifyPrincipalPolicyBundle({\n bundle: input.bundle,\n expectedReference: input.expectedReference,\n localCheckpoint: input.localCheckpoint ?? null,\n signerPublicKeys: input.signerPublicKeys,\n });\n if (verified.ok || verified.error.code !== \"unauthorized\") {\n return verified;\n }\n\n const externalAuthority = await input.loadExternalAuthority();\n if (!externalAuthority) {\n return verified;\n }\n\n const verifiedWithExternalAdmins = await verifyPrincipalPolicyBundle({\n bundle: input.bundle,\n externalAuthority,\n expectedReference: input.expectedReference,\n localCheckpoint: input.localCheckpoint ?? null,\n signerPublicKeys: input.signerPublicKeys,\n });\n\n return verifiedWithExternalAdmins;\n}\n"]}
1
+ {"version":3,"file":"principalPolicyAdminSigners.js","sourceRoot":"","sources":["principalPolicyAdminSigners.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type KeyingVerificationResult,\n type PrincipalPolicyCheckpoint,\n type PrincipalPolicyExternalAuthority,\n type PrincipalPolicySignerPublicKey,\n type ReferencedPrincipalHead,\n type VerifiedPrincipalPolicy,\n verifyPrincipalPolicyBundle,\n} from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\n\nexport function principalPolicyReferenceFromBundle(\n bundle: Pick<PrincipalPolicyBundleResponse, \"currentState\">,\n): ReferencedPrincipalHead {\n return {\n principalType: bundle.currentState.principalType,\n principalId: bundle.currentState.principalId,\n version: bundle.currentState.version,\n keyEpoch: bundle.currentState.keyEpoch,\n stateHash: bundle.currentState.stateHash,\n keyFingerprint: bundle.currentState.keyFingerprint,\n };\n}\n\nexport function organizationAdminSignerUserIds(\n policy: Pick<VerifiedPrincipalPolicy, \"projection\">,\n): string[] {\n return [\n ...new Set(\n policy.projection\n .filter((member) => member.role === \"admin\")\n .map((member) => member.userId),\n ),\n ].sort();\n}\n\nexport function organizationAdminExternalAuthority(\n policy: VerifiedPrincipalPolicy,\n): PrincipalPolicyExternalAuthority {\n const history = policy.history ?? [\n {\n state: policy.state,\n projection: policy.projection,\n grants: policy.grants,\n },\n ];\n const toHead = (state: (typeof history)[number][\"state\"]) => ({\n principalType: \"group\" as const,\n principalId: state.principalId,\n version: state.version,\n keyEpoch: state.keyEpoch,\n stateHash: state.stateHash,\n keyFingerprint: state.keyFingerprint,\n });\n return {\n currentHead: toHead(policy.state),\n states: history.map((entry) => ({\n head: toHead(entry.state),\n projection: entry.projection,\n })),\n };\n}\n\nexport async function verifyOrganizationAdminPolicy(input: {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly localCheckpoint?: PrincipalPolicyCheckpoint | null | undefined;\n readonly organizationId: string;\n readonly signerPublicKeys: readonly PrincipalPolicySignerPublicKey[];\n}): Promise<KeyingVerificationResult<VerifiedPrincipalPolicy>> {\n if (\n input.bundle.currentState.principalType !== \"organization\" ||\n input.bundle.currentState.principalId !== input.organizationId\n ) {\n return {\n ok: false,\n error: new KeyingVerificationError(\n \"object_mismatch\",\n \"organization policy does not match the expected organization\",\n ),\n };\n }\n\n return verifyPrincipalPolicyBundle({\n bundle: input.bundle,\n expectedReference: principalPolicyReferenceFromBundle(input.bundle),\n localCheckpoint: input.localCheckpoint ?? null,\n signerPublicKeys: input.signerPublicKeys,\n });\n}\n\nexport async function verifyPrincipalPolicyBundleWithExternalOrganizationAdmins(input: {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly expectedReference: ReferencedPrincipalHead;\n readonly loadExternalAuthority: () => Promise<PrincipalPolicyExternalAuthority | null>;\n readonly localCheckpoint?: PrincipalPolicyCheckpoint | null | undefined;\n readonly signerPublicKeys: readonly PrincipalPolicySignerPublicKey[];\n}): Promise<KeyingVerificationResult<VerifiedPrincipalPolicy>> {\n const verified = await verifyPrincipalPolicyBundle({\n bundle: input.bundle,\n expectedReference: input.expectedReference,\n localCheckpoint: input.localCheckpoint ?? null,\n signerPublicKeys: input.signerPublicKeys,\n });\n if (verified.ok || verified.error.code !== \"unauthorized\") {\n return verified;\n }\n\n const externalAuthority = await input.loadExternalAuthority();\n if (!externalAuthority) {\n return verified;\n }\n\n const verifiedWithExternalAdmins = await verifyPrincipalPolicyBundle({\n bundle: input.bundle,\n externalAuthority,\n expectedReference: input.expectedReference,\n localCheckpoint: input.localCheckpoint ?? null,\n signerPublicKeys: input.signerPublicKeys,\n });\n\n return verifiedWithExternalAdmins;\n}\n\n/**\n * Whether served Admins evidence could make this bundle verify. It re-runs\n * the verifier with an optimistic authority in which every Admins head the\n * chain cites lists its citing signer as an admin. A bundle that still fails\n * cannot be authorized by any evidence (for example a non-admin signer with\n * no external citation), so its refusal is a real verification failure\n * rather than missing evidence. The optimistic authority is never trusted\n * for anything else: the caller caches nothing either way.\n */\nexport async function externalAuthorityCouldAuthorize(input: {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly expectedReference: ReferencedPrincipalHead;\n readonly localCheckpoint?: PrincipalPolicyCheckpoint | null | undefined;\n readonly signerPublicKeys: readonly PrincipalPolicySignerPublicKey[];\n}): Promise<boolean> {\n const states = [\n input.bundle.currentState,\n ...input.bundle.previousStates.map((entry) => entry.state),\n ];\n const optimistic = new Map<\n string,\n PrincipalPolicyExternalAuthority[\"states\"][number]\n >();\n for (const state of states) {\n const head = state.externalAuthority;\n if (!head) continue;\n const key = JSON.stringify([\n head.principalType,\n head.principalId,\n head.version,\n head.keyEpoch,\n head.stateHash,\n head.keyFingerprint,\n ]);\n const projection = optimistic.get(key)?.projection ?? [];\n optimistic.set(key, {\n head,\n projection: projection.some(\n (member) => member.userId === state.signerUserId,\n )\n ? projection\n : [...projection, { userId: state.signerUserId, role: \"admin\" }],\n });\n }\n const [first] = optimistic.values();\n if (!first) return false;\n const verified = await verifyPrincipalPolicyBundle({\n bundle: input.bundle,\n expectedReference: input.expectedReference,\n externalAuthority: {\n currentHead: first.head,\n states: [...optimistic.values()],\n },\n localCheckpoint: input.localCheckpoint ?? null,\n signerPublicKeys: input.signerPublicKeys,\n });\n return verified.ok;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tearleads/client-sdk",
3
- "version": "0.1.104",
3
+ "version": "0.1.105",
4
4
  "description": "React-free Tearleads client runtime: local SQLite persistence, keys, sync, encrypted blobs, and domain workflows.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -1,5 +1,6 @@
1
1
  import { KeyingVerificationError, } from "../../../internal/crypto/index.js";
2
- import { verifyPrincipalPolicyBundleWithExternalOrganizationAdmins } from "../../../data/principals/principalPolicyAdminSigners.js";
2
+ import { ProjectionDependencyUnavailableError } from "../../../data/keyingProjectionVerification/dependencyUnavailable.js";
3
+ import { externalAuthorityCouldAuthorize, verifyPrincipalPolicyBundleWithExternalOrganizationAdmins, } from "../../../data/principals/principalPolicyAdminSigners.js";
3
4
  import { collectPrincipalPolicySignerPublicKeys, principalPolicyStates, } from "../policyVerification.js";
4
5
  function getBundleReferenceFailure(reference, bundle) {
5
6
  const states = principalPolicyStates(bundle);
@@ -131,7 +132,24 @@ export async function validatePrincipalPolicyBundleForCache(input) {
131
132
  expectedReference: input.reference,
132
133
  loadExternalAuthority: async () => {
133
134
  usedExternalAdminPolicy = true;
134
- return ((await input.loadExternalAdminPolicy())?.externalAuthority ?? null);
135
+ const authority = (await input.loadExternalAdminPolicy())
136
+ ?.externalAuthority;
137
+ if (authority)
138
+ return authority;
139
+ // The loader returns null when the organization's Admins policy could
140
+ // not be fetched, and an honest server always serves it. When that
141
+ // evidence is all the chain lacks, the gap is availability, not a
142
+ // refused signer, so it must not surface as an incident. A chain no
143
+ // evidence could authorize keeps its unauthorized refusal.
144
+ if (await externalAuthorityCouldAuthorize({
145
+ bundle: input.bundle,
146
+ expectedReference: input.reference,
147
+ localCheckpoint: input.localCheckpoint,
148
+ signerPublicKeys: signerPublicKeys.signerPublicKeys,
149
+ })) {
150
+ throw new ProjectionDependencyUnavailableError("Organization Admins evidence is unavailable");
151
+ }
152
+ return null;
135
153
  },
136
154
  localCheckpoint: input.localCheckpoint,
137
155
  signerPublicKeys: signerPublicKeys.signerPublicKeys,
@@ -1 +1 @@
1
- {"version":3,"file":"principalPolicyCacheValidation.js","sourceRoot":"","sources":["principalPolicyCacheValidation.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type KeyingVerificationCode,\n KeyingVerificationError,\n type PrincipalPolicyCheckpoint,\n type VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport type {\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport { verifyPrincipalPolicyBundleWithExternalOrganizationAdmins } from \"../../../data/principals/principalPolicyAdminSigners\";\nimport type { TrustedUserIdentityResolver } from \"../../../data/trustedUserIdentity\";\nimport type { VerifiedExternalAdminPolicy } from \"../externalAdminPolicy\";\nimport {\n collectPrincipalPolicySignerPublicKeys,\n type PrincipalPolicySignerPublicKeyLoadErrorCode,\n principalPolicyStates,\n} from \"../policyVerification\";\n\ninterface PrincipalPolicyValidationFailure {\n readonly code: KeyingVerificationCode;\n readonly message: string;\n}\n\nfunction getBundleReferenceFailure(\n reference: ReferencedPrincipalStateResponse,\n bundle: PrincipalPolicyBundleResponse,\n): PrincipalPolicyValidationFailure | null {\n const states = principalPolicyStates(bundle);\n const hasReferencedPrincipal = states.some(\n (state) =>\n state.principalType === reference.principalType &&\n state.principalId === reference.principalId,\n );\n if (!hasReferencedPrincipal) {\n return {\n code: \"object_mismatch\",\n message: \"bundle principal does not match referenced principal\",\n };\n }\n if (\n !states.some(\n (state) =>\n state.principalType === reference.principalType &&\n state.principalId === reference.principalId &&\n state.version === reference.version &&\n state.keyEpoch === reference.keyEpoch &&\n state.stateHash === reference.stateHash &&\n state.keyFingerprint === reference.keyFingerprint,\n )\n ) {\n return {\n code: \"hash_mismatch\",\n message: \"bundle chain does not contain referenced principal\",\n };\n }\n if (\n bundle.currentMemberEnvelopes.principalType !==\n bundle.currentState.principalType ||\n bundle.currentMemberEnvelopes.principalId !==\n bundle.currentState.principalId\n ) {\n return {\n code: \"object_mismatch\",\n message: \"member envelope principal does not match current principal\",\n };\n }\n if (\n bundle.currentMemberEnvelopes.stateHash !== bundle.currentState.stateHash\n ) {\n return {\n code: \"hash_mismatch\",\n message: \"member envelope state hash does not match current principal\",\n };\n }\n if (bundle.currentMemberEnvelopes.epoch !== bundle.currentState.keyEpoch) {\n return {\n code: \"hash_mismatch\",\n message: \"member envelope epoch does not match current principal\",\n };\n }\n if (\n bundle.currentPayload.principalType !== bundle.currentState.principalType ||\n bundle.currentPayload.principalId !== bundle.currentState.principalId\n ) {\n return {\n code: \"object_mismatch\",\n message: \"payload principal does not match current principal\",\n };\n }\n if (bundle.currentPayload.stateHash !== bundle.currentState.stateHash) {\n return {\n code: \"hash_mismatch\",\n message: \"payload state hash does not match current principal\",\n };\n }\n return null;\n}\n\nfunction getCheckpointFailure(\n checkpoint: PrincipalPolicyCheckpoint | null,\n bundle: PrincipalPolicyBundleResponse,\n): PrincipalPolicyValidationFailure | null {\n if (!checkpoint) {\n return null;\n }\n const current = bundle.currentState;\n if (current.version < checkpoint.version) {\n return {\n code: \"rollback\",\n message: \"principal policy state is older than the local checkpoint\",\n };\n }\n if (\n current.version === checkpoint.version &&\n current.stateHash !== checkpoint.stateHash\n ) {\n return {\n code: \"equivocation\",\n message: \"principal policy state conflicts with the local checkpoint\",\n };\n }\n if (current.version === checkpoint.version) {\n return null;\n }\n const checkpointState = principalPolicyStates(bundle).find(\n (state) => state.version === checkpoint.version,\n );\n return checkpointState?.stateHash === checkpoint.stateHash\n ? null\n : {\n code: \"stale_predecessor\",\n message: \"principal policy chain does not extend the local checkpoint\",\n };\n}\n\nfunction signerPublicKeyLoadFailure(\n code: PrincipalPolicySignerPublicKeyLoadErrorCode,\n): PrincipalPolicyValidationFailure {\n switch (code) {\n case \"fingerprint-mismatch\":\n return {\n code: \"signer_mismatch\",\n message: \"signer key fingerprint does not match state signer\",\n };\n case \"not-found\":\n // The identity source throws for transport failures, so an absent\n // signer here is the server asserting the cited user does not exist.\n return {\n code: \"missing_dependency\",\n message: \"signer identity is unknown to the server\",\n };\n }\n}\n\nfunction verificationError(\n failure: PrincipalPolicyValidationFailure,\n): KeyingVerificationError {\n return new KeyingVerificationError(failure.code, failure.message);\n}\n\nexport async function validatePrincipalPolicyBundleForCache(input: {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly loadExternalAdminPolicy: () => Promise<VerifiedExternalAdminPolicy | null>;\n readonly localCheckpoint: PrincipalPolicyCheckpoint | null;\n readonly reference: ReferencedPrincipalStateResponse;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n}): Promise<\n | {\n readonly externalAdminPolicy: VerifiedExternalAdminPolicy | null;\n readonly ok: true;\n readonly policy: VerifiedPrincipalPolicy;\n }\n | { readonly error: KeyingVerificationError; readonly ok: false }\n> {\n const referenceFailure = getBundleReferenceFailure(\n input.reference,\n input.bundle,\n );\n if (referenceFailure) {\n return { error: verificationError(referenceFailure), ok: false };\n }\n const checkpointFailure = getCheckpointFailure(\n input.localCheckpoint,\n input.bundle,\n );\n if (checkpointFailure) {\n return { error: verificationError(checkpointFailure), ok: false };\n }\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle: input.bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n return {\n error: verificationError(\n signerPublicKeyLoadFailure(signerPublicKeys.error),\n ),\n ok: false,\n };\n }\n\n let usedExternalAdminPolicy = false;\n const verified =\n await verifyPrincipalPolicyBundleWithExternalOrganizationAdmins({\n bundle: input.bundle,\n expectedReference: input.reference,\n loadExternalAuthority: async () => {\n usedExternalAdminPolicy = true;\n return (\n (await input.loadExternalAdminPolicy())?.externalAuthority ?? null\n );\n },\n localCheckpoint: input.localCheckpoint,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n return verified.ok\n ? {\n externalAdminPolicy: usedExternalAdminPolicy\n ? await input.loadExternalAdminPolicy()\n : null,\n ok: true,\n policy: verified.value,\n }\n : { error: verified.error, ok: false };\n}\n"]}
1
+ {"version":3,"file":"principalPolicyCacheValidation.js","sourceRoot":"","sources":["principalPolicyCacheValidation.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type KeyingVerificationCode,\n KeyingVerificationError,\n type PrincipalPolicyCheckpoint,\n type VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport type {\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport { ProjectionDependencyUnavailableError } from \"../../../data/keyingProjectionVerification/dependencyUnavailable\";\nimport {\n externalAuthorityCouldAuthorize,\n verifyPrincipalPolicyBundleWithExternalOrganizationAdmins,\n} from \"../../../data/principals/principalPolicyAdminSigners\";\nimport type { TrustedUserIdentityResolver } from \"../../../data/trustedUserIdentity\";\nimport type { VerifiedExternalAdminPolicy } from \"../externalAdminPolicy\";\nimport {\n collectPrincipalPolicySignerPublicKeys,\n type PrincipalPolicySignerPublicKeyLoadErrorCode,\n principalPolicyStates,\n} from \"../policyVerification\";\n\ninterface PrincipalPolicyValidationFailure {\n readonly code: KeyingVerificationCode;\n readonly message: string;\n}\n\nfunction getBundleReferenceFailure(\n reference: ReferencedPrincipalStateResponse,\n bundle: PrincipalPolicyBundleResponse,\n): PrincipalPolicyValidationFailure | null {\n const states = principalPolicyStates(bundle);\n const hasReferencedPrincipal = states.some(\n (state) =>\n state.principalType === reference.principalType &&\n state.principalId === reference.principalId,\n );\n if (!hasReferencedPrincipal) {\n return {\n code: \"object_mismatch\",\n message: \"bundle principal does not match referenced principal\",\n };\n }\n if (\n !states.some(\n (state) =>\n state.principalType === reference.principalType &&\n state.principalId === reference.principalId &&\n state.version === reference.version &&\n state.keyEpoch === reference.keyEpoch &&\n state.stateHash === reference.stateHash &&\n state.keyFingerprint === reference.keyFingerprint,\n )\n ) {\n return {\n code: \"hash_mismatch\",\n message: \"bundle chain does not contain referenced principal\",\n };\n }\n if (\n bundle.currentMemberEnvelopes.principalType !==\n bundle.currentState.principalType ||\n bundle.currentMemberEnvelopes.principalId !==\n bundle.currentState.principalId\n ) {\n return {\n code: \"object_mismatch\",\n message: \"member envelope principal does not match current principal\",\n };\n }\n if (\n bundle.currentMemberEnvelopes.stateHash !== bundle.currentState.stateHash\n ) {\n return {\n code: \"hash_mismatch\",\n message: \"member envelope state hash does not match current principal\",\n };\n }\n if (bundle.currentMemberEnvelopes.epoch !== bundle.currentState.keyEpoch) {\n return {\n code: \"hash_mismatch\",\n message: \"member envelope epoch does not match current principal\",\n };\n }\n if (\n bundle.currentPayload.principalType !== bundle.currentState.principalType ||\n bundle.currentPayload.principalId !== bundle.currentState.principalId\n ) {\n return {\n code: \"object_mismatch\",\n message: \"payload principal does not match current principal\",\n };\n }\n if (bundle.currentPayload.stateHash !== bundle.currentState.stateHash) {\n return {\n code: \"hash_mismatch\",\n message: \"payload state hash does not match current principal\",\n };\n }\n return null;\n}\n\nfunction getCheckpointFailure(\n checkpoint: PrincipalPolicyCheckpoint | null,\n bundle: PrincipalPolicyBundleResponse,\n): PrincipalPolicyValidationFailure | null {\n if (!checkpoint) {\n return null;\n }\n const current = bundle.currentState;\n if (current.version < checkpoint.version) {\n return {\n code: \"rollback\",\n message: \"principal policy state is older than the local checkpoint\",\n };\n }\n if (\n current.version === checkpoint.version &&\n current.stateHash !== checkpoint.stateHash\n ) {\n return {\n code: \"equivocation\",\n message: \"principal policy state conflicts with the local checkpoint\",\n };\n }\n if (current.version === checkpoint.version) {\n return null;\n }\n const checkpointState = principalPolicyStates(bundle).find(\n (state) => state.version === checkpoint.version,\n );\n return checkpointState?.stateHash === checkpoint.stateHash\n ? null\n : {\n code: \"stale_predecessor\",\n message: \"principal policy chain does not extend the local checkpoint\",\n };\n}\n\nfunction signerPublicKeyLoadFailure(\n code: PrincipalPolicySignerPublicKeyLoadErrorCode,\n): PrincipalPolicyValidationFailure {\n switch (code) {\n case \"fingerprint-mismatch\":\n return {\n code: \"signer_mismatch\",\n message: \"signer key fingerprint does not match state signer\",\n };\n case \"not-found\":\n // The identity source throws for transport failures, so an absent\n // signer here is the server asserting the cited user does not exist.\n return {\n code: \"missing_dependency\",\n message: \"signer identity is unknown to the server\",\n };\n }\n}\n\nfunction verificationError(\n failure: PrincipalPolicyValidationFailure,\n): KeyingVerificationError {\n return new KeyingVerificationError(failure.code, failure.message);\n}\n\nexport async function validatePrincipalPolicyBundleForCache(input: {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly loadExternalAdminPolicy: () => Promise<VerifiedExternalAdminPolicy | null>;\n readonly localCheckpoint: PrincipalPolicyCheckpoint | null;\n readonly reference: ReferencedPrincipalStateResponse;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n}): Promise<\n | {\n readonly externalAdminPolicy: VerifiedExternalAdminPolicy | null;\n readonly ok: true;\n readonly policy: VerifiedPrincipalPolicy;\n }\n | { readonly error: KeyingVerificationError; readonly ok: false }\n> {\n const referenceFailure = getBundleReferenceFailure(\n input.reference,\n input.bundle,\n );\n if (referenceFailure) {\n return { error: verificationError(referenceFailure), ok: false };\n }\n const checkpointFailure = getCheckpointFailure(\n input.localCheckpoint,\n input.bundle,\n );\n if (checkpointFailure) {\n return { error: verificationError(checkpointFailure), ok: false };\n }\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle: input.bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n return {\n error: verificationError(\n signerPublicKeyLoadFailure(signerPublicKeys.error),\n ),\n ok: false,\n };\n }\n\n let usedExternalAdminPolicy = false;\n const verified =\n await verifyPrincipalPolicyBundleWithExternalOrganizationAdmins({\n bundle: input.bundle,\n expectedReference: input.reference,\n loadExternalAuthority: async () => {\n usedExternalAdminPolicy = true;\n const authority = (await input.loadExternalAdminPolicy())\n ?.externalAuthority;\n if (authority) return authority;\n // The loader returns null when the organization's Admins policy could\n // not be fetched, and an honest server always serves it. When that\n // evidence is all the chain lacks, the gap is availability, not a\n // refused signer, so it must not surface as an incident. A chain no\n // evidence could authorize keeps its unauthorized refusal.\n if (\n await externalAuthorityCouldAuthorize({\n bundle: input.bundle,\n expectedReference: input.reference,\n localCheckpoint: input.localCheckpoint,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n })\n ) {\n throw new ProjectionDependencyUnavailableError(\n \"Organization Admins evidence is unavailable\",\n );\n }\n return null;\n },\n localCheckpoint: input.localCheckpoint,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n return verified.ok\n ? {\n externalAdminPolicy: usedExternalAdminPolicy\n ? await input.loadExternalAdminPolicy()\n : null,\n ok: true,\n policy: verified.value,\n }\n : { error: verified.error, ok: false };\n}\n"]}