@tearleads/client-sdk 0.1.109 → 0.1.110

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.
Files changed (40) hide show
  1. package/data/blobs/attachments/events.js +2 -3
  2. package/data/blobs/attachments/events.js.map +1 -1
  3. package/data/containers/shared/events.js +3 -4
  4. package/data/containers/shared/events.js.map +1 -1
  5. package/data/documents/shared/containerKek/containerKekPathHistory.js +3 -3
  6. package/data/documents/shared/containerKek/containerKekPathHistory.js.map +1 -1
  7. package/data/documents/shared/events.js +4 -4
  8. package/data/documents/shared/events.js.map +1 -1
  9. package/data/keying/canonicalJson.js +3 -65
  10. package/data/keying/canonicalJson.js.map +1 -1
  11. package/data/keyingProjectionVerification/checkpoints/bundleVerification.js +3 -3
  12. package/data/keyingProjectionVerification/checkpoints/bundleVerification.js.map +1 -1
  13. package/data/keyingProjectionVerification/checkpoints/verifiedBundleBytes.js +2 -3
  14. package/data/keyingProjectionVerification/checkpoints/verifiedBundleBytes.js.map +1 -1
  15. package/data/keyingProjectionVerification/container/containerManifestVerification.js +3 -3
  16. package/data/keyingProjectionVerification/container/containerManifestVerification.js.map +1 -1
  17. package/data/keyingProjectionVerification/document/documentManifestVerification.js +2 -3
  18. package/data/keyingProjectionVerification/document/documentManifestVerification.js.map +1 -1
  19. package/data/keyingProjectionVerification/documentPurge/documentPurgeDocumentChainVerification.js +2 -3
  20. package/data/keyingProjectionVerification/documentPurge/documentPurgeDocumentChainVerification.js.map +1 -1
  21. package/data/keyingProjectionVerification/readers.js +3 -3
  22. package/data/keyingProjectionVerification/readers.js.map +1 -1
  23. package/internal/crypto/index.d.ts +1 -1
  24. package/internal/crypto/index.js +1 -1
  25. package/internal/crypto/index.js.map +1 -1
  26. package/internal/crypto/keying/canonicalJsonGuard.d.ts +3 -0
  27. package/internal/crypto/keying/canonicalJsonGuard.js +60 -0
  28. package/internal/crypto/keying/canonicalJsonGuard.js.map +1 -0
  29. package/internal/crypto/keying/index.d.ts +1 -0
  30. package/internal/crypto/keying/index.js +1 -0
  31. package/internal/crypto/keying/index.js.map +1 -1
  32. package/package.json +1 -1
  33. package/workflows/blobs/verification/attachmentBindingVerification.js +3 -3
  34. package/workflows/blobs/verification/attachmentBindingVerification.js.map +1 -1
  35. package/workflows/containers/root/create.js +3 -3
  36. package/workflows/containers/root/create.js.map +1 -1
  37. package/workflows/documents/linkSet/linkSetBlobRewraps.js +2 -3
  38. package/workflows/documents/linkSet/linkSetBlobRewraps.js.map +1 -1
  39. package/workflows/documents/purge/index.js +3 -3
  40. package/workflows/documents/purge/index.js.map +1 -1
@@ -1,6 +1,5 @@
1
- import { CONTENT_RECORD_ENCRYPTION_SUITE, computeAccessEventBodyHash, computeContentRecordNonceDomainHash, computeWriteHeaderHash, signAccessEvent, signWriteHeader, } from "../../../internal/crypto/index.js";
1
+ import { CONTENT_RECORD_ENCRYPTION_SUITE, computeAccessEventBodyHash, computeContentRecordNonceDomainHash, computeWriteHeaderHash, readKeyingCanonicalJson, signAccessEvent, signWriteHeader, } from "../../../internal/crypto/index.js";
2
2
  import { uniqueSortedStrings } from "../../documents/shared/readers.js";
3
- import { readCanonicalJson } from "../../keying/canonicalJson.js";
4
3
  async function signBlobAttachmentBodyEvent(body, input) {
5
4
  const bodyLabel = body.eventType === "attachment.bind"
6
5
  ? "Blob attachment bind body"
@@ -17,7 +16,7 @@ async function signBlobAttachmentBodyEvent(body, input) {
17
16
  input.manifestIdentity.manifestHash,
18
17
  ...input.authorizingContainerPathRefs.flatMap((path) => path.map((ref) => ref.manifestHash)),
19
18
  ]),
20
- bodyHash: await computeAccessEventBodyHash(readCanonicalJson(body, bodyLabel)),
19
+ bodyHash: await computeAccessEventBodyHash(readKeyingCanonicalJson(body, bodyLabel)),
21
20
  signerUserId: input.author.signerUserId,
22
21
  signerDeviceId: input.author.signerDeviceId,
23
22
  signerKeyFingerprint: input.author.signerKeyFingerprint,
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sourceRoot":"","sources":["events.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type AccessEvent,\n type AttachmentBindAccessEventBody,\n type AttachmentDetachAccessEventBody,\n CONTENT_RECORD_ENCRYPTION_SUITE,\n computeAccessEventBodyHash,\n computeContentRecordNonceDomainHash,\n computeWriteHeaderHash,\n signAccessEvent,\n signWriteHeader,\n type UnsignedAccessEvent,\n type WriteHeader,\n} from \"@tearleads/crypto\";\nimport type { ContainerManifestRef } from \"@tearleads/validators/request\";\nimport { uniqueSortedStrings } from \"../../documents/shared/readers\";\nimport type { DocumentCreateAuthor } from \"../../documents/shared/types\";\nimport { readCanonicalJson } from \"../../keying/canonicalJson\";\nimport type { BlobEncryptionPlan, DocumentManifestIdentity } from \"./types\";\n\nasync function signBlobAttachmentBodyEvent<\n Body extends AttachmentBindAccessEventBody | AttachmentDetachAccessEventBody,\n>(\n body: Body,\n input: {\n author: DocumentCreateAuthor;\n blobId: string;\n eventId: string;\n manifestIdentity: DocumentManifestIdentity;\n signedAt: string;\n authorizingContainerPathRefs: readonly (readonly ContainerManifestRef[])[];\n },\n): Promise<{ body: Body; event: AccessEvent }> {\n const bodyLabel =\n body.eventType === \"attachment.bind\"\n ? \"Blob attachment bind body\"\n : \"Blob attachment detach body\";\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId: input.eventId,\n eventType: body.eventType,\n objectKind: \"blob\",\n objectId: input.blobId,\n organizationId: input.manifestIdentity.organizationId,\n previousManifestHash: null,\n dependencyManifestHashes: uniqueSortedStrings([\n input.manifestIdentity.manifestHash,\n ...input.authorizingContainerPathRefs.flatMap((path) =>\n path.map((ref) => ref.manifestHash),\n ),\n ]),\n bodyHash: await computeAccessEventBodyHash(\n readCanonicalJson(body, bodyLabel),\n ),\n signerUserId: input.author.signerUserId,\n signerDeviceId: input.author.signerDeviceId,\n signerKeyFingerprint: input.author.signerKeyFingerprint,\n signedAt: input.signedAt,\n };\n\n return {\n body,\n event: await signAccessEvent(unsignedEvent, input.author.signerPrivateKey),\n };\n}\n\nexport async function signBlobAttachmentEvent(input: {\n author: DocumentCreateAuthor;\n bindingId: string;\n blobId: string;\n documentId: string;\n eventId: string;\n expectedBindingId: string | null;\n manifestIdentity: DocumentManifestIdentity;\n signedAt: string;\n slotId: string;\n authorizingContainerPathRefs: readonly (readonly ContainerManifestRef[])[];\n}): Promise<{ body: AttachmentBindAccessEventBody; event: AccessEvent }> {\n return signBlobAttachmentBodyEvent(\n {\n eventType: \"attachment.bind\",\n bindingId: input.bindingId,\n blobId: input.blobId,\n documentId: input.documentId,\n slotId: input.slotId,\n expectedBindingId: input.expectedBindingId,\n documentManifestHash: input.manifestIdentity.manifestHash,\n },\n input,\n );\n}\n\nexport async function signBlobAttachmentDetachEvent(input: {\n author: DocumentCreateAuthor;\n bindingId: string;\n blobId: string;\n documentId: string;\n eventId: string;\n manifestIdentity: DocumentManifestIdentity;\n signedAt: string;\n slotId: string;\n authorizingContainerPathRefs: readonly (readonly ContainerManifestRef[])[];\n}): Promise<{ body: AttachmentDetachAccessEventBody; event: AccessEvent }> {\n return signBlobAttachmentBodyEvent(\n {\n eventType: \"attachment.detach\",\n bindingId: input.bindingId,\n blobId: input.blobId,\n documentId: input.documentId,\n slotId: input.slotId,\n documentManifestHash: input.manifestIdentity.manifestHash,\n },\n input,\n );\n}\n\nexport async function signBlobAttachmentWriteHeader(input: {\n authorizingContainerPathRefs: readonly (readonly ContainerManifestRef[])[];\n author: DocumentCreateAuthor;\n blobAccessManifestHash: string;\n blobId: string;\n contentKeyEpoch: number;\n encrypted: BlobEncryptionPlan;\n manifestIdentity: DocumentManifestIdentity;\n signedAt: string;\n targetHash: string;\n}): Promise<{ writeHeader: WriteHeader; writeHeaderHash: string }> {\n const writeHeader = await signWriteHeader(\n {\n version: 1,\n dependencyManifestHashes: uniqueSortedStrings(\n input.authorizingContainerPathRefs.flatMap((path) =>\n path.map((ref) => ref.manifestHash),\n ),\n ),\n organizationId: input.manifestIdentity.organizationId,\n objectKind: \"blob\",\n objectId: input.blobId,\n accessManifestHash: input.blobAccessManifestHash,\n contentKeyEpoch: input.contentKeyEpoch,\n targetHash: input.targetHash,\n encryptionSuite: CONTENT_RECORD_ENCRYPTION_SUITE,\n contentRecordId: input.blobId,\n nonceDomainHash: await computeContentRecordNonceDomainHash({\n version: 1,\n organizationId: input.manifestIdentity.organizationId,\n objectKind: \"blob\",\n objectId: input.blobId,\n contentKeyEpoch: input.contentKeyEpoch,\n encryptionSuite: CONTENT_RECORD_ENCRYPTION_SUITE,\n contentRecordId: input.blobId,\n }),\n metadataHash: input.encrypted.metadataHash,\n ciphertextHash: input.encrypted.sha256,\n writerUserId: input.author.signerUserId,\n writerDeviceId: input.author.signerDeviceId,\n writerKeyFingerprint: input.author.signerKeyFingerprint,\n signedAt: input.signedAt,\n },\n input.author.signerPrivateKey,\n );\n\n return {\n writeHeader,\n writeHeaderHash: await computeWriteHeaderHash(writeHeader),\n };\n}\n"]}
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["events.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type AccessEvent,\n type AttachmentBindAccessEventBody,\n type AttachmentDetachAccessEventBody,\n CONTENT_RECORD_ENCRYPTION_SUITE,\n computeAccessEventBodyHash,\n computeContentRecordNonceDomainHash,\n computeWriteHeaderHash,\n readKeyingCanonicalJson,\n signAccessEvent,\n signWriteHeader,\n type UnsignedAccessEvent,\n type WriteHeader,\n} from \"@tearleads/crypto\";\nimport type { ContainerManifestRef } from \"@tearleads/validators/request\";\nimport { uniqueSortedStrings } from \"../../documents/shared/readers\";\nimport type { DocumentCreateAuthor } from \"../../documents/shared/types\";\nimport type { BlobEncryptionPlan, DocumentManifestIdentity } from \"./types\";\n\nasync function signBlobAttachmentBodyEvent<\n Body extends AttachmentBindAccessEventBody | AttachmentDetachAccessEventBody,\n>(\n body: Body,\n input: {\n author: DocumentCreateAuthor;\n blobId: string;\n eventId: string;\n manifestIdentity: DocumentManifestIdentity;\n signedAt: string;\n authorizingContainerPathRefs: readonly (readonly ContainerManifestRef[])[];\n },\n): Promise<{ body: Body; event: AccessEvent }> {\n const bodyLabel =\n body.eventType === \"attachment.bind\"\n ? \"Blob attachment bind body\"\n : \"Blob attachment detach body\";\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId: input.eventId,\n eventType: body.eventType,\n objectKind: \"blob\",\n objectId: input.blobId,\n organizationId: input.manifestIdentity.organizationId,\n previousManifestHash: null,\n dependencyManifestHashes: uniqueSortedStrings([\n input.manifestIdentity.manifestHash,\n ...input.authorizingContainerPathRefs.flatMap((path) =>\n path.map((ref) => ref.manifestHash),\n ),\n ]),\n bodyHash: await computeAccessEventBodyHash(\n readKeyingCanonicalJson(body, bodyLabel),\n ),\n signerUserId: input.author.signerUserId,\n signerDeviceId: input.author.signerDeviceId,\n signerKeyFingerprint: input.author.signerKeyFingerprint,\n signedAt: input.signedAt,\n };\n\n return {\n body,\n event: await signAccessEvent(unsignedEvent, input.author.signerPrivateKey),\n };\n}\n\nexport async function signBlobAttachmentEvent(input: {\n author: DocumentCreateAuthor;\n bindingId: string;\n blobId: string;\n documentId: string;\n eventId: string;\n expectedBindingId: string | null;\n manifestIdentity: DocumentManifestIdentity;\n signedAt: string;\n slotId: string;\n authorizingContainerPathRefs: readonly (readonly ContainerManifestRef[])[];\n}): Promise<{ body: AttachmentBindAccessEventBody; event: AccessEvent }> {\n return signBlobAttachmentBodyEvent(\n {\n eventType: \"attachment.bind\",\n bindingId: input.bindingId,\n blobId: input.blobId,\n documentId: input.documentId,\n slotId: input.slotId,\n expectedBindingId: input.expectedBindingId,\n documentManifestHash: input.manifestIdentity.manifestHash,\n },\n input,\n );\n}\n\nexport async function signBlobAttachmentDetachEvent(input: {\n author: DocumentCreateAuthor;\n bindingId: string;\n blobId: string;\n documentId: string;\n eventId: string;\n manifestIdentity: DocumentManifestIdentity;\n signedAt: string;\n slotId: string;\n authorizingContainerPathRefs: readonly (readonly ContainerManifestRef[])[];\n}): Promise<{ body: AttachmentDetachAccessEventBody; event: AccessEvent }> {\n return signBlobAttachmentBodyEvent(\n {\n eventType: \"attachment.detach\",\n bindingId: input.bindingId,\n blobId: input.blobId,\n documentId: input.documentId,\n slotId: input.slotId,\n documentManifestHash: input.manifestIdentity.manifestHash,\n },\n input,\n );\n}\n\nexport async function signBlobAttachmentWriteHeader(input: {\n authorizingContainerPathRefs: readonly (readonly ContainerManifestRef[])[];\n author: DocumentCreateAuthor;\n blobAccessManifestHash: string;\n blobId: string;\n contentKeyEpoch: number;\n encrypted: BlobEncryptionPlan;\n manifestIdentity: DocumentManifestIdentity;\n signedAt: string;\n targetHash: string;\n}): Promise<{ writeHeader: WriteHeader; writeHeaderHash: string }> {\n const writeHeader = await signWriteHeader(\n {\n version: 1,\n dependencyManifestHashes: uniqueSortedStrings(\n input.authorizingContainerPathRefs.flatMap((path) =>\n path.map((ref) => ref.manifestHash),\n ),\n ),\n organizationId: input.manifestIdentity.organizationId,\n objectKind: \"blob\",\n objectId: input.blobId,\n accessManifestHash: input.blobAccessManifestHash,\n contentKeyEpoch: input.contentKeyEpoch,\n targetHash: input.targetHash,\n encryptionSuite: CONTENT_RECORD_ENCRYPTION_SUITE,\n contentRecordId: input.blobId,\n nonceDomainHash: await computeContentRecordNonceDomainHash({\n version: 1,\n organizationId: input.manifestIdentity.organizationId,\n objectKind: \"blob\",\n objectId: input.blobId,\n contentKeyEpoch: input.contentKeyEpoch,\n encryptionSuite: CONTENT_RECORD_ENCRYPTION_SUITE,\n contentRecordId: input.blobId,\n }),\n metadataHash: input.encrypted.metadataHash,\n ciphertextHash: input.encrypted.sha256,\n writerUserId: input.author.signerUserId,\n writerDeviceId: input.author.signerDeviceId,\n writerKeyFingerprint: input.author.signerKeyFingerprint,\n signedAt: input.signedAt,\n },\n input.author.signerPrivateKey,\n );\n\n return {\n writeHeader,\n writeHeaderHash: await computeWriteHeaderHash(writeHeader),\n };\n}\n"]}
@@ -1,5 +1,4 @@
1
- import { computeAccessEventBodyHash, computeAccessEventHash, computeAccessManifestHash, computeContainerKekMaterialId, deriveContainerAccessManifest, signAccessEvent, } from "../../../internal/crypto/index.js";
2
- import { readCanonicalJson } from "../../keying/canonicalJson.js";
1
+ import { computeAccessEventBodyHash, computeAccessEventHash, computeAccessManifestHash, computeContainerKekMaterialId, deriveContainerAccessManifest, readKeyingCanonicalJson, signAccessEvent, } from "../../../internal/crypto/index.js";
3
2
  import { uniqueSortedManifestHashes } from "./projection.js";
4
3
  export function buildContainerCreateBody(input) {
5
4
  return {
@@ -15,7 +14,7 @@ export function buildContainerCreateBody(input) {
15
14
  };
16
15
  }
17
16
  export async function signContainerCreateEvent(input) {
18
- const bodyHash = await computeAccessEventBodyHash(readCanonicalJson(input.body, "Container create body"));
17
+ const bodyHash = await computeAccessEventBodyHash(readKeyingCanonicalJson(input.body, "Container create body"));
19
18
  const unsignedEvent = {
20
19
  version: 1,
21
20
  eventId: input.eventId,
@@ -38,7 +37,7 @@ export async function signContainerCreateEvent(input) {
38
37
  };
39
38
  }
40
39
  export async function signContainerMutationEvent(input) {
41
- const bodyHash = await computeAccessEventBodyHash(readCanonicalJson(input.body, "Container mutation body"));
40
+ const bodyHash = await computeAccessEventBodyHash(readKeyingCanonicalJson(input.body, "Container mutation body"));
42
41
  const unsignedEvent = {
43
42
  version: 1,
44
43
  eventId: input.eventId,
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sourceRoot":"","sources":["events.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type ContainerAccessManifestState,\n type ContainerCreateAccessEventBody,\n type ContainerGrantAccessEventBody,\n type ContainerKekRecipientTarget,\n type ContainerKeyEpoch,\n type ContainerMoveAccessEventBody,\n type ContainerReciteAccessEventBody,\n type ContainerRekeyAccessEventBody,\n type ContainerRevokeAccessEventBody,\n computeAccessEventBodyHash,\n computeAccessEventHash,\n computeAccessManifestHash,\n computeContainerKekMaterialId,\n deriveContainerAccessManifest,\n signAccessEvent,\n type UnsignedAccessEvent,\n} from \"@tearleads/crypto\";\nimport type {\n ContainerKekResponse,\n ContainerWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { readCanonicalJson } from \"../../keying/canonicalJson\";\nimport { uniqueSortedManifestHashes } from \"./projection\";\nimport type {\n ContainerCreatePlan,\n ContainerMovePlan,\n ContainerMutationAuthor,\n ContainerRevokePlan,\n ContainerSharePlan,\n} from \"./types\";\n\nexport function buildContainerCreateBody(input: {\n containerKeyEpochId: string;\n containerKeyPublicKey: string;\n metadataDocumentId: string;\n systemSlot?: string | null | undefined;\n parentContainerId: string | null;\n parentManifestHash: string | null;\n}): ContainerCreateAccessEventBody {\n return {\n systemSlot: input.systemSlot ?? null,\n eventType: \"container.create\",\n parentContainerId: input.parentContainerId,\n parentManifestHash: input.parentManifestHash,\n metadataDocumentId: input.metadataDocumentId,\n containerKeyEpochId: input.containerKeyEpochId,\n containerKeyPublicKey: input.containerKeyPublicKey,\n directGrants: [],\n referencedPrincipalHeads: [],\n };\n}\n\nexport async function signContainerCreateEvent(input: {\n author: ContainerMutationAuthor;\n body: ContainerCreateAccessEventBody;\n containerId: string;\n eventId: string;\n // The organization the new container belongs to — always the parent\n // container's org, NOT the author's. A member writing under another org's\n // shared root creates a container owned by that org; only the signer identity\n // comes from the author. The server trusts this signed field, so it is the\n // load-bearing org for the create.\n organizationId: string;\n parentPath: ContainerWriterProjectionResponse[\"path\"];\n signedAt: string;\n}): Promise<Pick<ContainerCreatePlan, \"event\" | \"eventHash\">> {\n const bodyHash = await computeAccessEventBodyHash(\n readCanonicalJson(input.body, \"Container create body\"),\n );\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId: input.eventId,\n eventType: \"container.create\",\n objectKind: \"container\",\n objectId: input.containerId,\n organizationId: input.organizationId,\n previousManifestHash: null,\n dependencyManifestHashes: uniqueSortedManifestHashes(input.parentPath),\n bodyHash,\n signerUserId: input.author.signerUserId,\n signerDeviceId: input.author.signerDeviceId,\n signerKeyFingerprint: input.author.signerKeyFingerprint,\n signedAt: input.signedAt,\n };\n const event = await signAccessEvent(\n unsignedEvent,\n input.author.signerPrivateKey,\n );\n\n return {\n event,\n eventHash: await computeAccessEventHash(event),\n };\n}\n\nexport async function signContainerMutationEvent(input: {\n author: ContainerMutationAuthor;\n body:\n | ContainerGrantAccessEventBody\n | ContainerMoveAccessEventBody\n | ContainerRekeyAccessEventBody\n | ContainerReciteAccessEventBody\n | ContainerRevokeAccessEventBody;\n containerId: string;\n dependencyManifestHashes: readonly string[];\n eventId: string;\n previousManifestHash: string;\n signedAt: string;\n}): Promise<\n Pick<\n ContainerSharePlan | ContainerMovePlan | ContainerRevokePlan,\n \"event\" | \"eventHash\"\n >\n> {\n const bodyHash = await computeAccessEventBodyHash(\n readCanonicalJson(input.body, \"Container mutation body\"),\n );\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId: input.eventId,\n eventType: input.body.eventType,\n objectKind: \"container\",\n objectId: input.containerId,\n organizationId: input.author.organizationId,\n previousManifestHash: input.previousManifestHash,\n dependencyManifestHashes: [\n ...new Set(input.dependencyManifestHashes),\n ].sort(),\n bodyHash,\n signerUserId: input.author.signerUserId,\n signerDeviceId: input.author.signerDeviceId,\n signerKeyFingerprint: input.author.signerKeyFingerprint,\n signedAt: input.signedAt,\n };\n const event = await signAccessEvent(\n unsignedEvent,\n input.author.signerPrivateKey,\n );\n\n return {\n event,\n eventHash: await computeAccessEventHash(event),\n };\n}\n\nexport async function deriveContainerCreateManifest(input: {\n containerId: string;\n containerKeyEpochId: string;\n containerKeyPublicKey: string;\n // Grants baked into the create manifest. Defaults to none. A child container\n // born with a managed-principal (group/organization) grant — e.g. the org\n // public metadata container granted to the Members group — supplies both the\n // grant and its referenced principal head here so the manifest hash reconciles\n // with the signed event body and the server can derive the KEK recipient target.\n directGrants?: ContainerCreateAccessEventBody[\"directGrants\"] | undefined;\n eventHash: string;\n metadataDocumentId: string;\n systemSlot?: string | null | undefined;\n // The parent container's org (see signContainerCreateEvent). Must match the\n // signed event's org or the manifest hash will not reconcile server-side.\n organizationId: string;\n parentContainerId: string | null;\n parentManifestHash: string | null;\n referencedPrincipalHeads?:\n | ContainerCreateAccessEventBody[\"referencedPrincipalHeads\"]\n | undefined;\n}): Promise<Pick<ContainerCreatePlan, \"manifest\" | \"manifestHash\" | \"state\">> {\n const state: ContainerAccessManifestState = {\n systemSlot: input.systemSlot ?? null,\n version: 1,\n containerId: input.containerId,\n organizationId: input.organizationId,\n epoch: 1,\n previousManifestHash: null,\n eventHash: input.eventHash,\n parentContainerId: input.parentContainerId,\n parentManifestHash: input.parentManifestHash,\n metadataDocumentId: input.metadataDocumentId,\n containerKeyEpochId: input.containerKeyEpochId,\n containerKeyPublicKey: input.containerKeyPublicKey,\n directGrants: input.directGrants ? [...input.directGrants] : [],\n referencedPrincipalHeads: input.referencedPrincipalHeads\n ? [...input.referencedPrincipalHeads]\n : [],\n };\n const manifest = await deriveContainerAccessManifest(state);\n\n return {\n manifest,\n manifestHash: await computeAccessManifestHash(manifest),\n state,\n };\n}\n\nexport function buildContainerCreateKeyEpoch(input: {\n containerId: string;\n containerKeyEpochId: string;\n eventHash: string;\n /** Defaults to the creation epoch; rotations pass their next epoch. */\n keyEpoch?: number | undefined;\n manifestHash: string;\n parentContainerKeyEpochId: string | null;\n}): ContainerKeyEpoch {\n return {\n id: input.containerKeyEpochId,\n containerId: input.containerId,\n keyEpoch: input.keyEpoch ?? 1,\n accessManifestHash: input.manifestHash,\n parentContainerKeyEpochId: input.parentContainerKeyEpochId,\n createdByEventHash: input.eventHash,\n createdByManifestHash: input.manifestHash,\n };\n}\n\nexport function buildParentRecipientTargets(\n parentKek: ContainerKekResponse,\n): ContainerKekRecipientTarget[] {\n return [\n {\n recipientKind: \"container\",\n recipientId: parentKek.containerId,\n recipientKeyEpochId: parentKek.containerKeyEpochId,\n recipientKeyFingerprint: parentKek.keyEpochHash,\n },\n ];\n}\n\nexport async function resolveContainerKekEpochId(input: {\n containerId: string;\n keyEpoch: number;\n keyMaterial: Uint8Array;\n override?: string | undefined;\n}): Promise<string> {\n const materialId = await computeContainerKekMaterialId({\n containerId: input.containerId,\n keyEpoch: input.keyEpoch,\n keyMaterial: input.keyMaterial,\n });\n if (input.override !== undefined && input.override !== materialId) {\n throw new Error(\n \"Container key epoch id does not match the committed KEK material\",\n );\n }\n\n return materialId;\n}\n"]}
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["events.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,0BAA0B,EAC1B,sBAAsB,EACtB,yBAAyB,EACzB,6BAA6B,EAC7B,6BAA6B,EAC7B,uBAAuB,EACvB,eAAe,GAEhB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAS1D,MAAM,UAAU,wBAAwB,CAAC,KAOxC;IACC,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;QACpC,SAAS,EAAE,kBAAkB;QAC7B,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;QAC1C,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;QAC5C,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;QAC5C,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;QAC9C,qBAAqB,EAAE,KAAK,CAAC,qBAAqB;QAClD,YAAY,EAAE,EAAE;QAChB,wBAAwB,EAAE,EAAE;KAC7B,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,KAa9C;IACC,MAAM,QAAQ,GAAG,MAAM,0BAA0B,CAC/C,uBAAuB,CAAC,KAAK,CAAC,IAAI,EAAE,uBAAuB,CAAC,CAC7D,CAAC;IACF,MAAM,aAAa,GAAwB;QACzC,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS,EAAE,kBAAkB;QAC7B,UAAU,EAAE,WAAW;QACvB,QAAQ,EAAE,KAAK,CAAC,WAAW;QAC3B,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,oBAAoB,EAAE,IAAI;QAC1B,wBAAwB,EAAE,0BAA0B,CAAC,KAAK,CAAC,UAAU,CAAC;QACtE,QAAQ;QACR,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC,YAAY;QACvC,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,cAAc;QAC3C,oBAAoB,EAAE,KAAK,CAAC,MAAM,CAAC,oBAAoB;QACvD,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAC;IACF,MAAM,KAAK,GAAG,MAAM,eAAe,CACjC,aAAa,EACb,KAAK,CAAC,MAAM,CAAC,gBAAgB,CAC9B,CAAC;IAEF,OAAO;QACL,KAAK;QACL,SAAS,EAAE,MAAM,sBAAsB,CAAC,KAAK,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,KAahD;IAMC,MAAM,QAAQ,GAAG,MAAM,0BAA0B,CAC/C,uBAAuB,CAAC,KAAK,CAAC,IAAI,EAAE,yBAAyB,CAAC,CAC/D,CAAC;IACF,MAAM,aAAa,GAAwB;QACzC,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,SAAS;QAC/B,UAAU,EAAE,WAAW;QACvB,QAAQ,EAAE,KAAK,CAAC,WAAW;QAC3B,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,cAAc;QAC3C,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;QAChD,wBAAwB,EAAE;YACxB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,wBAAwB,CAAC;SAC3C,CAAC,IAAI,EAAE;QACR,QAAQ;QACR,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC,YAAY;QACvC,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,cAAc;QAC3C,oBAAoB,EAAE,KAAK,CAAC,MAAM,CAAC,oBAAoB;QACvD,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAC;IACF,MAAM,KAAK,GAAG,MAAM,eAAe,CACjC,aAAa,EACb,KAAK,CAAC,MAAM,CAAC,gBAAgB,CAC9B,CAAC;IAEF,OAAO;QACL,KAAK;QACL,SAAS,EAAE,MAAM,sBAAsB,CAAC,KAAK,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAAC,KAqBnD;IACC,MAAM,KAAK,GAAiC;QAC1C,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;QACpC,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,KAAK,EAAE,CAAC;QACR,oBAAoB,EAAE,IAAI;QAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;QAC1C,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;QAC5C,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;QAC5C,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;QAC9C,qBAAqB,EAAE,KAAK,CAAC,qBAAqB;QAClD,YAAY,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE;QAC/D,wBAAwB,EAAE,KAAK,CAAC,wBAAwB;YACtD,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,wBAAwB,CAAC;YACrC,CAAC,CAAC,EAAE;KACP,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,6BAA6B,CAAC,KAAK,CAAC,CAAC;IAE5D,OAAO;QACL,QAAQ;QACR,YAAY,EAAE,MAAM,yBAAyB,CAAC,QAAQ,CAAC;QACvD,KAAK;KACN,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,KAQ5C;IACC,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,mBAAmB;QAC7B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,CAAC;QAC7B,kBAAkB,EAAE,KAAK,CAAC,YAAY;QACtC,yBAAyB,EAAE,KAAK,CAAC,yBAAyB;QAC1D,kBAAkB,EAAE,KAAK,CAAC,SAAS;QACnC,qBAAqB,EAAE,KAAK,CAAC,YAAY;KAC1C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,SAA+B;IAE/B,OAAO;QACL;YACE,aAAa,EAAE,WAAW;YAC1B,WAAW,EAAE,SAAS,CAAC,WAAW;YAClC,mBAAmB,EAAE,SAAS,CAAC,mBAAmB;YAClD,uBAAuB,EAAE,SAAS,CAAC,YAAY;SAChD;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,KAKhD;IACC,MAAM,UAAU,GAAG,MAAM,6BAA6B,CAAC;QACrD,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,WAAW,EAAE,KAAK,CAAC,WAAW;KAC/B,CAAC,CAAC;IACH,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CACb,kEAAkE,CACnE,CAAC;IACJ,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC","sourcesContent":["import {\n type ContainerAccessManifestState,\n type ContainerCreateAccessEventBody,\n type ContainerGrantAccessEventBody,\n type ContainerKekRecipientTarget,\n type ContainerKeyEpoch,\n type ContainerMoveAccessEventBody,\n type ContainerReciteAccessEventBody,\n type ContainerRekeyAccessEventBody,\n type ContainerRevokeAccessEventBody,\n computeAccessEventBodyHash,\n computeAccessEventHash,\n computeAccessManifestHash,\n computeContainerKekMaterialId,\n deriveContainerAccessManifest,\n readKeyingCanonicalJson,\n signAccessEvent,\n type UnsignedAccessEvent,\n} from \"@tearleads/crypto\";\nimport type {\n ContainerKekResponse,\n ContainerWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { uniqueSortedManifestHashes } from \"./projection\";\nimport type {\n ContainerCreatePlan,\n ContainerMovePlan,\n ContainerMutationAuthor,\n ContainerRevokePlan,\n ContainerSharePlan,\n} from \"./types\";\n\nexport function buildContainerCreateBody(input: {\n containerKeyEpochId: string;\n containerKeyPublicKey: string;\n metadataDocumentId: string;\n systemSlot?: string | null | undefined;\n parentContainerId: string | null;\n parentManifestHash: string | null;\n}): ContainerCreateAccessEventBody {\n return {\n systemSlot: input.systemSlot ?? null,\n eventType: \"container.create\",\n parentContainerId: input.parentContainerId,\n parentManifestHash: input.parentManifestHash,\n metadataDocumentId: input.metadataDocumentId,\n containerKeyEpochId: input.containerKeyEpochId,\n containerKeyPublicKey: input.containerKeyPublicKey,\n directGrants: [],\n referencedPrincipalHeads: [],\n };\n}\n\nexport async function signContainerCreateEvent(input: {\n author: ContainerMutationAuthor;\n body: ContainerCreateAccessEventBody;\n containerId: string;\n eventId: string;\n // The organization the new container belongs to — always the parent\n // container's org, NOT the author's. A member writing under another org's\n // shared root creates a container owned by that org; only the signer identity\n // comes from the author. The server trusts this signed field, so it is the\n // load-bearing org for the create.\n organizationId: string;\n parentPath: ContainerWriterProjectionResponse[\"path\"];\n signedAt: string;\n}): Promise<Pick<ContainerCreatePlan, \"event\" | \"eventHash\">> {\n const bodyHash = await computeAccessEventBodyHash(\n readKeyingCanonicalJson(input.body, \"Container create body\"),\n );\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId: input.eventId,\n eventType: \"container.create\",\n objectKind: \"container\",\n objectId: input.containerId,\n organizationId: input.organizationId,\n previousManifestHash: null,\n dependencyManifestHashes: uniqueSortedManifestHashes(input.parentPath),\n bodyHash,\n signerUserId: input.author.signerUserId,\n signerDeviceId: input.author.signerDeviceId,\n signerKeyFingerprint: input.author.signerKeyFingerprint,\n signedAt: input.signedAt,\n };\n const event = await signAccessEvent(\n unsignedEvent,\n input.author.signerPrivateKey,\n );\n\n return {\n event,\n eventHash: await computeAccessEventHash(event),\n };\n}\n\nexport async function signContainerMutationEvent(input: {\n author: ContainerMutationAuthor;\n body:\n | ContainerGrantAccessEventBody\n | ContainerMoveAccessEventBody\n | ContainerRekeyAccessEventBody\n | ContainerReciteAccessEventBody\n | ContainerRevokeAccessEventBody;\n containerId: string;\n dependencyManifestHashes: readonly string[];\n eventId: string;\n previousManifestHash: string;\n signedAt: string;\n}): Promise<\n Pick<\n ContainerSharePlan | ContainerMovePlan | ContainerRevokePlan,\n \"event\" | \"eventHash\"\n >\n> {\n const bodyHash = await computeAccessEventBodyHash(\n readKeyingCanonicalJson(input.body, \"Container mutation body\"),\n );\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId: input.eventId,\n eventType: input.body.eventType,\n objectKind: \"container\",\n objectId: input.containerId,\n organizationId: input.author.organizationId,\n previousManifestHash: input.previousManifestHash,\n dependencyManifestHashes: [\n ...new Set(input.dependencyManifestHashes),\n ].sort(),\n bodyHash,\n signerUserId: input.author.signerUserId,\n signerDeviceId: input.author.signerDeviceId,\n signerKeyFingerprint: input.author.signerKeyFingerprint,\n signedAt: input.signedAt,\n };\n const event = await signAccessEvent(\n unsignedEvent,\n input.author.signerPrivateKey,\n );\n\n return {\n event,\n eventHash: await computeAccessEventHash(event),\n };\n}\n\nexport async function deriveContainerCreateManifest(input: {\n containerId: string;\n containerKeyEpochId: string;\n containerKeyPublicKey: string;\n // Grants baked into the create manifest. Defaults to none. A child container\n // born with a managed-principal (group/organization) grant — e.g. the org\n // public metadata container granted to the Members group — supplies both the\n // grant and its referenced principal head here so the manifest hash reconciles\n // with the signed event body and the server can derive the KEK recipient target.\n directGrants?: ContainerCreateAccessEventBody[\"directGrants\"] | undefined;\n eventHash: string;\n metadataDocumentId: string;\n systemSlot?: string | null | undefined;\n // The parent container's org (see signContainerCreateEvent). Must match the\n // signed event's org or the manifest hash will not reconcile server-side.\n organizationId: string;\n parentContainerId: string | null;\n parentManifestHash: string | null;\n referencedPrincipalHeads?:\n | ContainerCreateAccessEventBody[\"referencedPrincipalHeads\"]\n | undefined;\n}): Promise<Pick<ContainerCreatePlan, \"manifest\" | \"manifestHash\" | \"state\">> {\n const state: ContainerAccessManifestState = {\n systemSlot: input.systemSlot ?? null,\n version: 1,\n containerId: input.containerId,\n organizationId: input.organizationId,\n epoch: 1,\n previousManifestHash: null,\n eventHash: input.eventHash,\n parentContainerId: input.parentContainerId,\n parentManifestHash: input.parentManifestHash,\n metadataDocumentId: input.metadataDocumentId,\n containerKeyEpochId: input.containerKeyEpochId,\n containerKeyPublicKey: input.containerKeyPublicKey,\n directGrants: input.directGrants ? [...input.directGrants] : [],\n referencedPrincipalHeads: input.referencedPrincipalHeads\n ? [...input.referencedPrincipalHeads]\n : [],\n };\n const manifest = await deriveContainerAccessManifest(state);\n\n return {\n manifest,\n manifestHash: await computeAccessManifestHash(manifest),\n state,\n };\n}\n\nexport function buildContainerCreateKeyEpoch(input: {\n containerId: string;\n containerKeyEpochId: string;\n eventHash: string;\n /** Defaults to the creation epoch; rotations pass their next epoch. */\n keyEpoch?: number | undefined;\n manifestHash: string;\n parentContainerKeyEpochId: string | null;\n}): ContainerKeyEpoch {\n return {\n id: input.containerKeyEpochId,\n containerId: input.containerId,\n keyEpoch: input.keyEpoch ?? 1,\n accessManifestHash: input.manifestHash,\n parentContainerKeyEpochId: input.parentContainerKeyEpochId,\n createdByEventHash: input.eventHash,\n createdByManifestHash: input.manifestHash,\n };\n}\n\nexport function buildParentRecipientTargets(\n parentKek: ContainerKekResponse,\n): ContainerKekRecipientTarget[] {\n return [\n {\n recipientKind: \"container\",\n recipientId: parentKek.containerId,\n recipientKeyEpochId: parentKek.containerKeyEpochId,\n recipientKeyFingerprint: parentKek.keyEpochHash,\n },\n ];\n}\n\nexport async function resolveContainerKekEpochId(input: {\n containerId: string;\n keyEpoch: number;\n keyMaterial: Uint8Array;\n override?: string | undefined;\n}): Promise<string> {\n const materialId = await computeContainerKekMaterialId({\n containerId: input.containerId,\n keyEpoch: input.keyEpoch,\n keyMaterial: input.keyMaterial,\n });\n if (input.override !== undefined && input.override !== materialId) {\n throw new Error(\n \"Container key epoch id does not match the committed KEK material\",\n );\n }\n\n return materialId;\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { computeContainerKekKeyringHash, computeContainerKekMaterialId, isContainerKekMaterialId, KeyingVerificationError, normalizeContainerAccessEventBody, normalizeContainerKekKeyring, openContainerKekKeyring, } from "../../../../internal/crypto/index.js";
2
- import { readCanonicalJson, readCanonicalRecord, } from "../../../keying/canonicalJson.js";
1
+ import { computeContainerKekKeyringHash, computeContainerKekMaterialId, isContainerKekMaterialId, KeyingVerificationError, normalizeContainerAccessEventBody, normalizeContainerKekKeyring, openContainerKekKeyring, readKeyingCanonicalJson, } from "../../../../internal/crypto/index.js";
2
+ import { readCanonicalRecord } from "../../../keying/canonicalJson.js";
3
3
  import { readContainerKeyEpoch } from "../../../keyingProjectionVerification/readers.js";
4
4
  import { readManifestContainerId } from "../readers.js";
5
5
  import { verifyContainerKekEntries } from "./containerKekEntryVerification.js";
@@ -42,7 +42,7 @@ async function assertKeyringCommitment(input) {
42
42
  throw new Error(`${projectionKekLabel(input.index)} keyring signer is missing`);
43
43
  }
44
44
  const eventBundle = readCanonicalRecord(rotationManifest.event, `${projectionKekLabel(input.index)} keyring event`);
45
- const eventBody = normalizeContainerAccessEventBody(readCanonicalJson(Reflect.get(eventBundle, "body"), `${projectionKekLabel(input.index)} keyring event body`));
45
+ const eventBody = normalizeContainerAccessEventBody(readKeyingCanonicalJson(Reflect.get(eventBundle, "body"), `${projectionKekLabel(input.index)} keyring event body`));
46
46
  if ((eventBody.eventType !== "container.move" &&
47
47
  eventBody.eventType !== "container.rekey" &&
48
48
  eventBody.eventType !== "container.revoke") ||
@@ -1 +1 @@
1
- {"version":3,"file":"containerKekPathHistory.js","sourceRoot":"","sources":["containerKekPathHistory.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n computeContainerKekKeyringHash,\n computeContainerKekMaterialId,\n isContainerKekMaterialId,\n KeyingVerificationError,\n normalizeContainerAccessEventBody,\n normalizeContainerKekKeyring,\n openContainerKekKeyring,\n type VerifiedContainerAccessManifest,\n} from \"@tearleads/crypto\";\nimport type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport {\n readCanonicalJson,\n readCanonicalRecord,\n} from \"../../../keying/canonicalJson\";\nimport { readContainerKeyEpoch } from \"../../../keyingProjectionVerification/readers\";\nimport { readManifestContainerId } from \"../readers\";\nimport type { UnwrappedContainerKek } from \"../types\";\nimport { verifyContainerKekEntries } from \"./containerKekEntryVerification\";\n\ntype ProjectionKek = ContainerWriterProjectionResponse[\"containerKeks\"][number];\ntype VerifiedManifests = ReadonlyMap<string, VerifiedContainerAccessManifest>;\n\n/** The projection proves a predecessor existed, but no retained keyring can recover it. */\nexport class ContainerKekHistoryUnavailableError extends Error {\n constructor(label: string) {\n super(`${label} keyring is missing`);\n this.name = \"ContainerKekHistoryUnavailableError\";\n }\n}\n\nexport function projectionKekLabel(index: number): string {\n return `Container writer projection KEK[${index}]`;\n}\n\nexport async function assertUnwrappedContainerKekMatchesMaterialId(input: {\n label: string;\n keyMaterial: Uint8Array;\n kek: Pick<\n ProjectionKek,\n \"containerId\" | \"containerKeyEpoch\" | \"containerKeyEpochId\"\n >;\n}): Promise<void> {\n if (!isContainerKekMaterialId(input.kek.containerKeyEpochId)) {\n throw new Error(\n `${input.label} KEK epoch id does not commit to key material`,\n );\n }\n const expectedId = await computeContainerKekMaterialId({\n containerId: input.kek.containerId,\n keyEpoch: input.kek.containerKeyEpoch,\n keyMaterial: input.keyMaterial,\n });\n if (expectedId !== input.kek.containerKeyEpochId) {\n throw new Error(\n `${input.label} KEK material does not match committed epoch id`,\n );\n }\n}\n\nfunction sameContainerManifestHistory(kek: ProjectionKek) {\n return kek.containerManifestHistory.filter(\n (bundle) => readManifestContainerId(bundle) === kek.containerId,\n );\n}\n\n/**\n * Proves the served keyring is the one the rotation that minted the current\n * epoch committed to in its signed event body.\n */\nasync function assertKeyringCommitment(input: {\n currentManifest: ContainerWriterProjectionResponse[\"path\"][number];\n index: number;\n kek: ProjectionKek;\n keyring: ReturnType<typeof normalizeContainerKekKeyring>;\n}): Promise<void> {\n const rotationManifestHash = readContainerKeyEpoch(\n input.kek.keyEpoch,\n `${projectionKekLabel(input.index)} key epoch`,\n ).accessManifestHash;\n const rotationManifest =\n rotationManifestHash === input.currentManifest.manifestHash\n ? input.currentManifest\n : sameContainerManifestHistory(input.kek).find(\n (bundle) => bundle.manifestHash === rotationManifestHash,\n );\n if (!rotationManifest) {\n throw new Error(\n `${projectionKekLabel(input.index)} keyring signer is missing`,\n );\n }\n const eventBundle = readCanonicalRecord(\n rotationManifest.event,\n `${projectionKekLabel(input.index)} keyring event`,\n );\n const eventBody = normalizeContainerAccessEventBody(\n readCanonicalJson(\n Reflect.get(eventBundle, \"body\"),\n `${projectionKekLabel(input.index)} keyring event body`,\n ),\n );\n if (\n (eventBody.eventType !== \"container.move\" &&\n eventBody.eventType !== \"container.rekey\" &&\n eventBody.eventType !== \"container.revoke\") ||\n eventBody.keyringHash !==\n (await computeContainerKekKeyringHash(input.keyring))\n ) {\n throw new Error(\n `${projectionKekLabel(input.index)} keyring does not match its signed event`,\n );\n }\n}\n\nasync function openVerifiedKeyringEntries(input: {\n currentManifest: ContainerWriterProjectionResponse[\"path\"][number];\n index: number;\n kek: ProjectionKek;\n successorKeyMaterial: Uint8Array;\n verifiedByHash: VerifiedManifests;\n verifyKeyringCommitment: boolean;\n}): Promise<Awaited<ReturnType<typeof openContainerKekKeyring>>> {\n const { kek } = input;\n const keyring = normalizeContainerKekKeyring(kek.keyring);\n if (\n keyring.containerId !== kek.containerId ||\n keyring.containerKeyEpochId !== kek.containerKeyEpochId\n ) {\n throw new Error(\n `${projectionKekLabel(input.index)} keyring is inconsistent`,\n );\n }\n if (input.verifyKeyringCommitment) {\n await assertKeyringCommitment({\n currentManifest: input.currentManifest,\n index: input.index,\n kek,\n keyring,\n });\n }\n\n let entries: Awaited<ReturnType<typeof openContainerKekKeyring>>;\n try {\n entries = await openContainerKekKeyring({\n keyEpoch: kek.containerKeyEpoch,\n keyring,\n successorContainerKey: input.successorKeyMaterial,\n });\n } catch (error) {\n if (error instanceof KeyingVerificationError) {\n throw error;\n }\n // Label opaque AEAD failures; structural verification errors already\n // carry their own diagnosis.\n throw new Error(\n `${projectionKekLabel(input.index)} keyring could not be opened`,\n { cause: error },\n );\n }\n // Per-entry material-id verification proves an entry's key matches the id\n // it declares, but a poisoned keyring can declare a self-consistent FAKE\n // id — fresh key, matching commitment — for an epoch the container never\n // had. Anchor every entry the projection can name to the epoch id its\n // signed history actually committed; anything else is a forged epoch.\n await verifyContainerKekEntries(kek.containerId, entries);\n\n // Every epoch id the signed manifest history names must be present in the\n // keyring. A forged entry can be self-consistent, but it cannot also make\n // the real epoch id — which a signed state already named — disappear.\n const entryEpochIds = new Set(\n entries.map((entry) => entry.containerKeyEpochId),\n );\n const signedEpochIds = signedHistoryEpochIds({\n headManifestHash: input.currentManifest.manifestHash,\n kek,\n verifiedByHash: input.verifiedByHash,\n });\n for (const historicalEpochId of signedEpochIds) {\n if (!entryEpochIds.has(historicalEpochId)) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n `${projectionKekLabel(input.index)} keyring omits an epoch its manifest history commits to`,\n );\n }\n }\n return entries;\n}\n\n/**\n * Epoch ids named by verified historical manifest states for this container.\n * Those states are signed, so an id they name is an epoch the container\n * provably had — and a keyring that omits it in favor of a forged id for the\n * same position is detectable even when no epoch RECORD was shipped.\n */\nexport function manifestHistoryEpochIds(kek: ProjectionKek): Set<string> {\n const epochIds = new Set<string>();\n for (const bundle of sameContainerManifestHistory(kek)) {\n const state = readCanonicalRecord(\n bundle.state,\n \"Container writer projection historical manifest state\",\n );\n const containerKeyEpochId = Reflect.get(state, \"containerKeyEpochId\");\n if (\n typeof containerKeyEpochId === \"string\" &&\n containerKeyEpochId !== kek.containerKeyEpochId\n ) {\n epochIds.add(containerKeyEpochId);\n }\n }\n return epochIds;\n}\n\n/**\n * The epoch ids this KEK's own history names, plus every one its head's\n * verified lineage names wherever the projection served it. Verification pools\n * every KEK's history, so a server can serve a container's older manifests\n * under another KEK; walking the lineage keeps a forged keyring id from passing\n * because the real id it displaces moved (#2365 finding 32). The walk stops at\n * the first manifest verification did not record, so it only ever adds ids.\n */\nexport function signedHistoryEpochIds(input: {\n readonly headManifestHash: string;\n readonly kek: ProjectionKek;\n readonly verifiedByHash: VerifiedManifests;\n}): Set<string> {\n const epochIds = manifestHistoryEpochIds(input.kek);\n const visited = new Set<string>();\n let current = input.verifiedByHash.get(input.headManifestHash);\n while (\n current &&\n current.state.containerId === input.kek.containerId &&\n !visited.has(current.manifestHash)\n ) {\n visited.add(current.manifestHash);\n const epochId = current.state.containerKeyEpochId;\n if (epochId !== null && epochId !== input.kek.containerKeyEpochId) {\n epochIds.add(epochId);\n }\n const previousHash = current.state.previousManifestHash;\n current =\n previousHash === null\n ? undefined\n : input.verifiedByHash.get(previousHash);\n }\n return epochIds;\n}\n\nfunction admitKeyringEntry(input: {\n entry: Awaited<ReturnType<typeof openContainerKekKeyring>>[number];\n index: number;\n kek: ProjectionKek;\n keksByEpochId: Map<string, UnwrappedContainerKek>;\n}): void {\n const { entry, kek } = input;\n const cached = input.keksByEpochId.get(entry.containerKeyEpochId);\n if (cached) {\n if (cached.containerId !== kek.containerId) {\n throw new Error(\n `${projectionKekLabel(input.index)} cached historical KEK is inconsistent`,\n );\n }\n return;\n }\n input.keksByEpochId.set(entry.containerKeyEpochId, {\n containerId: kek.containerId,\n // A keyring entry is key material plus the epoch id that commits to it —\n // there is no epoch record, so no fingerprint. Selection falls back to the\n // epoch id and AEAD authenticates; the commitment check above is what\n // makes the material itself trustworthy.\n keyEpochHash: null,\n keyMaterial: entry.keyMaterial,\n });\n}\n\n/**\n * Opens the sealed keyring for one path index and admits every entry into\n * `keksByEpochId` after verifying it against the material-id commitment its\n * ordinal position implies (entry i is key epoch i + 1). No chain walk:\n * one decrypt yields the container's complete retained history, and entry\n * verification is independent per entry.\n */\nexport async function unwrapKeyringContainerKeksAtIndex(input: {\n currentManifest: ContainerWriterProjectionResponse[\"path\"][number];\n index: number;\n kek: ProjectionKek;\n keksByEpochId: Map<string, UnwrappedContainerKek>;\n successorKeyMaterial: Uint8Array | null;\n /** Every manifest projection verification recorded; anchors the keyring. */\n verifiedByHash: VerifiedManifests;\n verifyKeyringCommitment: boolean;\n}): Promise<void> {\n const { kek, successorKeyMaterial } = input;\n if (!successorKeyMaterial) {\n return;\n }\n\n if (kek.keyring === null) {\n if (kek.containerKeyEpoch !== 1) {\n throw new ContainerKekHistoryUnavailableError(\n projectionKekLabel(input.index),\n );\n }\n return;\n }\n\n const entries = await openVerifiedKeyringEntries({\n currentManifest: input.currentManifest,\n index: input.index,\n kek,\n successorKeyMaterial,\n verifiedByHash: input.verifiedByHash,\n verifyKeyringCommitment: input.verifyKeyringCommitment,\n });\n for (const entry of entries) {\n admitKeyringEntry({\n entry,\n index: input.index,\n kek,\n keksByEpochId: input.keksByEpochId,\n });\n }\n}\n\n/**\n * Epoch ids this projection implies exist but that were not recovered —\n * used to attribute history failures to the material they orphan.\n */\nexport function projectedUnreachablePredecessorEpochIds(input: {\n kek: ProjectionKek;\n keksByEpochId: ReadonlyMap<string, UnwrappedContainerKek>;\n}): string[] {\n const projectedEpochIds = new Set<string>();\n for (const bundle of sameContainerManifestHistory(input.kek)) {\n const state = readCanonicalRecord(\n bundle.state,\n \"Container writer projection historical manifest state\",\n );\n const containerKeyEpochId = Reflect.get(state, \"containerKeyEpochId\");\n if (typeof containerKeyEpochId === \"string\") {\n projectedEpochIds.add(containerKeyEpochId);\n }\n }\n projectedEpochIds.delete(input.kek.containerKeyEpochId);\n return [...projectedEpochIds].filter(\n (containerKeyEpochId) => !input.keksByEpochId.has(containerKeyEpochId),\n );\n}\n"]}
1
+ {"version":3,"file":"containerKekPathHistory.js","sourceRoot":"","sources":["containerKekPathHistory.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n computeContainerKekKeyringHash,\n computeContainerKekMaterialId,\n isContainerKekMaterialId,\n KeyingVerificationError,\n normalizeContainerAccessEventBody,\n normalizeContainerKekKeyring,\n openContainerKekKeyring,\n readKeyingCanonicalJson,\n type VerifiedContainerAccessManifest,\n} from \"@tearleads/crypto\";\nimport type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport { readCanonicalRecord } from \"../../../keying/canonicalJson\";\nimport { readContainerKeyEpoch } from \"../../../keyingProjectionVerification/readers\";\nimport { readManifestContainerId } from \"../readers\";\nimport type { UnwrappedContainerKek } from \"../types\";\nimport { verifyContainerKekEntries } from \"./containerKekEntryVerification\";\n\ntype ProjectionKek = ContainerWriterProjectionResponse[\"containerKeks\"][number];\ntype VerifiedManifests = ReadonlyMap<string, VerifiedContainerAccessManifest>;\n\n/** The projection proves a predecessor existed, but no retained keyring can recover it. */\nexport class ContainerKekHistoryUnavailableError extends Error {\n constructor(label: string) {\n super(`${label} keyring is missing`);\n this.name = \"ContainerKekHistoryUnavailableError\";\n }\n}\n\nexport function projectionKekLabel(index: number): string {\n return `Container writer projection KEK[${index}]`;\n}\n\nexport async function assertUnwrappedContainerKekMatchesMaterialId(input: {\n label: string;\n keyMaterial: Uint8Array;\n kek: Pick<\n ProjectionKek,\n \"containerId\" | \"containerKeyEpoch\" | \"containerKeyEpochId\"\n >;\n}): Promise<void> {\n if (!isContainerKekMaterialId(input.kek.containerKeyEpochId)) {\n throw new Error(\n `${input.label} KEK epoch id does not commit to key material`,\n );\n }\n const expectedId = await computeContainerKekMaterialId({\n containerId: input.kek.containerId,\n keyEpoch: input.kek.containerKeyEpoch,\n keyMaterial: input.keyMaterial,\n });\n if (expectedId !== input.kek.containerKeyEpochId) {\n throw new Error(\n `${input.label} KEK material does not match committed epoch id`,\n );\n }\n}\n\nfunction sameContainerManifestHistory(kek: ProjectionKek) {\n return kek.containerManifestHistory.filter(\n (bundle) => readManifestContainerId(bundle) === kek.containerId,\n );\n}\n\n/**\n * Proves the served keyring is the one the rotation that minted the current\n * epoch committed to in its signed event body.\n */\nasync function assertKeyringCommitment(input: {\n currentManifest: ContainerWriterProjectionResponse[\"path\"][number];\n index: number;\n kek: ProjectionKek;\n keyring: ReturnType<typeof normalizeContainerKekKeyring>;\n}): Promise<void> {\n const rotationManifestHash = readContainerKeyEpoch(\n input.kek.keyEpoch,\n `${projectionKekLabel(input.index)} key epoch`,\n ).accessManifestHash;\n const rotationManifest =\n rotationManifestHash === input.currentManifest.manifestHash\n ? input.currentManifest\n : sameContainerManifestHistory(input.kek).find(\n (bundle) => bundle.manifestHash === rotationManifestHash,\n );\n if (!rotationManifest) {\n throw new Error(\n `${projectionKekLabel(input.index)} keyring signer is missing`,\n );\n }\n const eventBundle = readCanonicalRecord(\n rotationManifest.event,\n `${projectionKekLabel(input.index)} keyring event`,\n );\n const eventBody = normalizeContainerAccessEventBody(\n readKeyingCanonicalJson(\n Reflect.get(eventBundle, \"body\"),\n `${projectionKekLabel(input.index)} keyring event body`,\n ),\n );\n if (\n (eventBody.eventType !== \"container.move\" &&\n eventBody.eventType !== \"container.rekey\" &&\n eventBody.eventType !== \"container.revoke\") ||\n eventBody.keyringHash !==\n (await computeContainerKekKeyringHash(input.keyring))\n ) {\n throw new Error(\n `${projectionKekLabel(input.index)} keyring does not match its signed event`,\n );\n }\n}\n\nasync function openVerifiedKeyringEntries(input: {\n currentManifest: ContainerWriterProjectionResponse[\"path\"][number];\n index: number;\n kek: ProjectionKek;\n successorKeyMaterial: Uint8Array;\n verifiedByHash: VerifiedManifests;\n verifyKeyringCommitment: boolean;\n}): Promise<Awaited<ReturnType<typeof openContainerKekKeyring>>> {\n const { kek } = input;\n const keyring = normalizeContainerKekKeyring(kek.keyring);\n if (\n keyring.containerId !== kek.containerId ||\n keyring.containerKeyEpochId !== kek.containerKeyEpochId\n ) {\n throw new Error(\n `${projectionKekLabel(input.index)} keyring is inconsistent`,\n );\n }\n if (input.verifyKeyringCommitment) {\n await assertKeyringCommitment({\n currentManifest: input.currentManifest,\n index: input.index,\n kek,\n keyring,\n });\n }\n\n let entries: Awaited<ReturnType<typeof openContainerKekKeyring>>;\n try {\n entries = await openContainerKekKeyring({\n keyEpoch: kek.containerKeyEpoch,\n keyring,\n successorContainerKey: input.successorKeyMaterial,\n });\n } catch (error) {\n if (error instanceof KeyingVerificationError) {\n throw error;\n }\n // Label opaque AEAD failures; structural verification errors already\n // carry their own diagnosis.\n throw new Error(\n `${projectionKekLabel(input.index)} keyring could not be opened`,\n { cause: error },\n );\n }\n // Per-entry material-id verification proves an entry's key matches the id\n // it declares, but a poisoned keyring can declare a self-consistent FAKE\n // id — fresh key, matching commitment — for an epoch the container never\n // had. Anchor every entry the projection can name to the epoch id its\n // signed history actually committed; anything else is a forged epoch.\n await verifyContainerKekEntries(kek.containerId, entries);\n\n // Every epoch id the signed manifest history names must be present in the\n // keyring. A forged entry can be self-consistent, but it cannot also make\n // the real epoch id — which a signed state already named — disappear.\n const entryEpochIds = new Set(\n entries.map((entry) => entry.containerKeyEpochId),\n );\n const signedEpochIds = signedHistoryEpochIds({\n headManifestHash: input.currentManifest.manifestHash,\n kek,\n verifiedByHash: input.verifiedByHash,\n });\n for (const historicalEpochId of signedEpochIds) {\n if (!entryEpochIds.has(historicalEpochId)) {\n throw new KeyingVerificationError(\n \"missing_dependency\",\n `${projectionKekLabel(input.index)} keyring omits an epoch its manifest history commits to`,\n );\n }\n }\n return entries;\n}\n\n/**\n * Epoch ids named by verified historical manifest states for this container.\n * Those states are signed, so an id they name is an epoch the container\n * provably had — and a keyring that omits it in favor of a forged id for the\n * same position is detectable even when no epoch RECORD was shipped.\n */\nexport function manifestHistoryEpochIds(kek: ProjectionKek): Set<string> {\n const epochIds = new Set<string>();\n for (const bundle of sameContainerManifestHistory(kek)) {\n const state = readCanonicalRecord(\n bundle.state,\n \"Container writer projection historical manifest state\",\n );\n const containerKeyEpochId = Reflect.get(state, \"containerKeyEpochId\");\n if (\n typeof containerKeyEpochId === \"string\" &&\n containerKeyEpochId !== kek.containerKeyEpochId\n ) {\n epochIds.add(containerKeyEpochId);\n }\n }\n return epochIds;\n}\n\n/**\n * The epoch ids this KEK's own history names, plus every one its head's\n * verified lineage names wherever the projection served it. Verification pools\n * every KEK's history, so a server can serve a container's older manifests\n * under another KEK; walking the lineage keeps a forged keyring id from passing\n * because the real id it displaces moved (#2365 finding 32). The walk stops at\n * the first manifest verification did not record, so it only ever adds ids.\n */\nexport function signedHistoryEpochIds(input: {\n readonly headManifestHash: string;\n readonly kek: ProjectionKek;\n readonly verifiedByHash: VerifiedManifests;\n}): Set<string> {\n const epochIds = manifestHistoryEpochIds(input.kek);\n const visited = new Set<string>();\n let current = input.verifiedByHash.get(input.headManifestHash);\n while (\n current &&\n current.state.containerId === input.kek.containerId &&\n !visited.has(current.manifestHash)\n ) {\n visited.add(current.manifestHash);\n const epochId = current.state.containerKeyEpochId;\n if (epochId !== null && epochId !== input.kek.containerKeyEpochId) {\n epochIds.add(epochId);\n }\n const previousHash = current.state.previousManifestHash;\n current =\n previousHash === null\n ? undefined\n : input.verifiedByHash.get(previousHash);\n }\n return epochIds;\n}\n\nfunction admitKeyringEntry(input: {\n entry: Awaited<ReturnType<typeof openContainerKekKeyring>>[number];\n index: number;\n kek: ProjectionKek;\n keksByEpochId: Map<string, UnwrappedContainerKek>;\n}): void {\n const { entry, kek } = input;\n const cached = input.keksByEpochId.get(entry.containerKeyEpochId);\n if (cached) {\n if (cached.containerId !== kek.containerId) {\n throw new Error(\n `${projectionKekLabel(input.index)} cached historical KEK is inconsistent`,\n );\n }\n return;\n }\n input.keksByEpochId.set(entry.containerKeyEpochId, {\n containerId: kek.containerId,\n // A keyring entry is key material plus the epoch id that commits to it —\n // there is no epoch record, so no fingerprint. Selection falls back to the\n // epoch id and AEAD authenticates; the commitment check above is what\n // makes the material itself trustworthy.\n keyEpochHash: null,\n keyMaterial: entry.keyMaterial,\n });\n}\n\n/**\n * Opens the sealed keyring for one path index and admits every entry into\n * `keksByEpochId` after verifying it against the material-id commitment its\n * ordinal position implies (entry i is key epoch i + 1). No chain walk:\n * one decrypt yields the container's complete retained history, and entry\n * verification is independent per entry.\n */\nexport async function unwrapKeyringContainerKeksAtIndex(input: {\n currentManifest: ContainerWriterProjectionResponse[\"path\"][number];\n index: number;\n kek: ProjectionKek;\n keksByEpochId: Map<string, UnwrappedContainerKek>;\n successorKeyMaterial: Uint8Array | null;\n /** Every manifest projection verification recorded; anchors the keyring. */\n verifiedByHash: VerifiedManifests;\n verifyKeyringCommitment: boolean;\n}): Promise<void> {\n const { kek, successorKeyMaterial } = input;\n if (!successorKeyMaterial) {\n return;\n }\n\n if (kek.keyring === null) {\n if (kek.containerKeyEpoch !== 1) {\n throw new ContainerKekHistoryUnavailableError(\n projectionKekLabel(input.index),\n );\n }\n return;\n }\n\n const entries = await openVerifiedKeyringEntries({\n currentManifest: input.currentManifest,\n index: input.index,\n kek,\n successorKeyMaterial,\n verifiedByHash: input.verifiedByHash,\n verifyKeyringCommitment: input.verifyKeyringCommitment,\n });\n for (const entry of entries) {\n admitKeyringEntry({\n entry,\n index: input.index,\n kek,\n keksByEpochId: input.keksByEpochId,\n });\n }\n}\n\n/**\n * Epoch ids this projection implies exist but that were not recovered —\n * used to attribute history failures to the material they orphan.\n */\nexport function projectedUnreachablePredecessorEpochIds(input: {\n kek: ProjectionKek;\n keksByEpochId: ReadonlyMap<string, UnwrappedContainerKek>;\n}): string[] {\n const projectedEpochIds = new Set<string>();\n for (const bundle of sameContainerManifestHistory(input.kek)) {\n const state = readCanonicalRecord(\n bundle.state,\n \"Container writer projection historical manifest state\",\n );\n const containerKeyEpochId = Reflect.get(state, \"containerKeyEpochId\");\n if (typeof containerKeyEpochId === \"string\") {\n projectedEpochIds.add(containerKeyEpochId);\n }\n }\n projectedEpochIds.delete(input.kek.containerKeyEpochId);\n return [...projectedEpochIds].filter(\n (containerKeyEpochId) => !input.keksByEpochId.has(containerKeyEpochId),\n );\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { computeAccessEventBodyHash, computeAccessEventHash, computeAccessManifestHash, computeDocumentContentKeyTargetHash, deriveDocumentLinkSetManifest, signAccessEvent, } from "../../../internal/crypto/index.js";
2
- import { readCanonicalJson, readCanonicalRecord, } from "../../keying/canonicalJson.js";
1
+ import { computeAccessEventBodyHash, computeAccessEventHash, computeAccessManifestHash, computeDocumentContentKeyTargetHash, deriveDocumentLinkSetManifest, readKeyingCanonicalJson, signAccessEvent, } from "../../../internal/crypto/index.js";
2
+ import { readCanonicalRecord } from "../../keying/canonicalJson.js";
3
3
  import { authorizingContainerPathRefsForLinkSet, containerPathRefs, deriveDocumentCreateTargets, mergeTargetEnvelopes, } from "./projection/index.js";
4
4
  import { sortDocumentTargets, uniqueSortedStrings } from "./readers.js";
5
5
  export async function buildDocumentCreatePlan({ author, containerProjection, contentKeyEpoch = 1, documentId = crypto.randomUUID(), eventId = crypto.randomUUID(), signedAt = new Date().toISOString(), targetEnvelopes, }) {
@@ -20,7 +20,7 @@ export async function buildDocumentCreatePlan({ author, containerProjection, con
20
20
  containerId: targetContainerId,
21
21
  containerManifestHash: targetContainerManifestHash,
22
22
  };
23
- const bodyHash = await computeAccessEventBodyHash(readCanonicalJson(body, "Document create body"));
23
+ const bodyHash = await computeAccessEventBodyHash(readKeyingCanonicalJson(body, "Document create body"));
24
24
  const unsignedEvent = {
25
25
  version: 1,
26
26
  eventId,
@@ -83,7 +83,7 @@ export async function buildDocumentLinkSetEventPlan(input) {
83
83
  containerId: input.targetState.target.containerId,
84
84
  containerManifestHash: input.targetState.target.containerManifestHash,
85
85
  };
86
- const bodyHash = await computeAccessEventBodyHash(readCanonicalJson(body, "Document link-set body"));
86
+ const bodyHash = await computeAccessEventBodyHash(readKeyingCanonicalJson(body, "Document link-set body"));
87
87
  const authorizingContainerPathRefs = authorizingContainerPathRefsForLinkSet({
88
88
  operation: input.operation,
89
89
  targetContainerId: input.targetState.target.containerId,
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sourceRoot":"","sources":["events.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,yBAAyB,EACzB,mCAAmC,EAGnC,6BAA6B,EAC7B,eAAe,GAEhB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,sCAAsC,EACtC,iBAAiB,EACjB,2BAA2B,EAC3B,oBAAoB,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAWrE,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,EAC5C,MAAM,EACN,mBAAmB,EACnB,eAAe,GAAG,CAAC,EACnB,UAAU,GAAG,MAAM,CAAC,UAAU,EAAE,EAChC,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,EAC7B,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EACnC,eAAe,GACc;IAC7B,4EAA4E;IAC5E,6EAA6E;IAC7E,4EAA4E;IAC5E,MAAM,cAAc,GAAG,mBAAmB,CAAC,cAAc,CAAC;IAE1D,MAAM,OAAO,GAAG,2BAA2B,CAAC,mBAAmB,CAAC,CAAC;IACjE,MAAM,yBAAyB,GAAG,oBAAoB,CACpD,OAAO,EACP,eAAe,CAChB,CAAC;IACF,MAAM,2BAA2B,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,qBAAqB,CAAC;IACtE,MAAM,iBAAiB,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC;IAClD,IAAI,CAAC,2BAA2B,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,IAAI,GAAgC;QACxC,WAAW,EAAE,EAAE;QACf,SAAS,EAAE,eAAe;QAC1B,WAAW,EAAE,iBAAiB;QAC9B,qBAAqB,EAAE,2BAA2B;KACnD,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,0BAA0B,CAC/C,iBAAiB,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAChD,CAAC;IACF,MAAM,aAAa,GAAwB;QACzC,OAAO,EAAE,CAAC;QACV,OAAO;QACP,SAAS,EAAE,eAAe;QAC1B,UAAU,EAAE,UAAU;QACtB,QAAQ,EAAE,UAAU;QACpB,cAAc;QACd,oBAAoB,EAAE,IAAI;QAC1B,wBAAwB,EAAE,mBAAmB,CAC3C,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAC9D;QACD,QAAQ;QACR,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,oBAAoB,EAAE,MAAM,CAAC,oBAAoB;QACjD,QAAQ;KACT,CAAC;IACF,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,aAAa,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAC5E,MAAM,SAAS,GAAG,MAAM,sBAAsB,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,KAAK,GAAiC;QAC1C,OAAO,EAAE,CAAC;QACV,UAAU;QACV,cAAc;QACd,KAAK,EAAE,CAAC;QACR,oBAAoB,EAAE,IAAI;QAC1B,SAAS;QACT,kBAAkB,EAAE,CAAC,iBAAiB,CAAC;KACxC,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,6BAA6B,CAAC,KAAK,CAAC,CAAC;IAC5D,MAAM,YAAY,GAAG,MAAM,yBAAyB,CAAC,QAAQ,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAG,MAAM,mCAAmC,CAAC,OAAO,CAAC,CAAC;IAEtE,OAAO;QACL,IAAI;QACJ,UAAU;QACV,KAAK;QACL,SAAS;QACT,QAAQ;QACR,YAAY;QACZ,OAAO,EAAE;YACP,KAAK,EAAE,mBAAmB,CAAC,KAAK,EAAE,uBAAuB,CAAC;YAC1D,IAAI,EAAE,mBAAmB,CAAC,IAAI,EAAE,sBAAsB,CAAC;YACvD,oBAAoB,EAAE,YAAY;YAClC,QAAQ,EAAE,mBAAmB,CAAC,QAAQ,EAAE,0BAA0B,CAAC;YACnE,uBAAuB,EAAE,iBAAiB,CAAC,mBAAmB,CAAC,IAAI,CAAC;YACpE,gBAAgB,EAAE;gBAChB,eAAe;gBACf,mBAAmB,EAAE,YAAY;gBACjC,UAAU;gBACV,OAAO,EAAE,yBAAyB;aACnC;SACF;QACD,KAAK;QACL,UAAU;QACV,OAAO,EAAE,mBAAmB,CAAC,OAAO,CAAC;KACtC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAAC,KAanD;IACC,MAAM,SAAS,GACb,KAAK,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,iBAAiB,CAAC;IACnE,MAAM,IAAI,GAAgC;QACxC,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,SAAS;QACT,WAAW,EAAE,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW;QACjD,qBAAqB,EAAE,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,qBAAqB;KACtE,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,0BAA0B,CAC/C,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,CAAC,CAClD,CAAC;IACF,MAAM,4BAA4B,GAAG,sCAAsC,CAAC;QAC1E,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,iBAAiB,EAAE,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW;QACvD,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;KACzC,CAAC,CAAC;IACH,MAAM,wBAAwB,GAAG,mBAAmB,CAClD;QACE,GAAG,KAAK,CAAC,uBAAuB;QAChC,GAAG,4BAA4B,CAAC,IAAI,EAAE;KACvC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,CACjC,CAAC;IACF,MAAM,aAAa,GAAwB;QACzC,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS;QACT,UAAU,EAAE,UAAU;QACtB,QAAQ,EAAE,KAAK,CAAC,gBAAgB,CAAC,UAAU;QAC3C,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,oBAAoB,EAAE,KAAK,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,YAAY;QAC1E,wBAAwB;QACxB,QAAQ;QACR,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC,YAAY;QACvC,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,cAAc;QAC3C,oBAAoB,EAAE,KAAK,CAAC,MAAM,CAAC,oBAAoB;QACvD,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAC;IACF,MAAM,KAAK,GAAG,MAAM,eAAe,CACjC,aAAa,EACb,KAAK,CAAC,MAAM,CAAC,gBAAgB,CAC9B,CAAC;IACF,MAAM,SAAS,GAAG,MAAM,sBAAsB,CAAC,KAAK,CAAC,CAAC;IAEtD,OAAO;QACL,4BAA4B;QAC5B,IAAI;QACJ,KAAK;QACL,SAAS;KACV,CAAC;AACJ,CAAC","sourcesContent":["import {\n computeAccessEventBodyHash,\n computeAccessEventHash,\n computeAccessManifestHash,\n computeDocumentContentKeyTargetHash,\n type DocumentLinkAccessEventBody,\n type DocumentLinkSetManifestState,\n deriveDocumentLinkSetManifest,\n signAccessEvent,\n type UnsignedAccessEvent,\n} from \"@tearleads/crypto\";\nimport type { ContainerManifestRef } from \"@tearleads/validators/request\";\nimport type { DocumentWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport {\n readCanonicalJson,\n readCanonicalRecord,\n} from \"../../keying/canonicalJson\";\nimport {\n authorizingContainerPathRefsForLinkSet,\n containerPathRefs,\n deriveDocumentCreateTargets,\n mergeTargetEnvelopes,\n} from \"./projection\";\nimport { sortDocumentTargets, uniqueSortedStrings } from \"./readers\";\nimport type {\n BuildDocumentCreatePlanInput,\n DocumentCreateAuthor,\n DocumentCreatePlan,\n DocumentLinkSetEventPlan,\n DocumentLinkSetMutationBody,\n DocumentLinkSetMutationOperation,\n DocumentLinkSetTargetState,\n} from \"./types\";\n\nexport async function buildDocumentCreatePlan({\n author,\n containerProjection,\n contentKeyEpoch = 1,\n documentId = crypto.randomUUID(),\n eventId = crypto.randomUUID(),\n signedAt = new Date().toISOString(),\n targetEnvelopes,\n}: BuildDocumentCreatePlanInput): Promise<DocumentCreatePlan> {\n // The document belongs to its container's organization, not the author's. A\n // member authoring a document under another org's shared container creates a\n // document owned by that org; the author supplies only the signer identity.\n const organizationId = containerProjection.organizationId;\n\n const targets = deriveDocumentCreateTargets(containerProjection);\n const targetEnvelopesForRequest = mergeTargetEnvelopes(\n targets,\n targetEnvelopes,\n );\n const targetContainerManifestHash = targets[0]?.containerManifestHash;\n const targetContainerId = targets[0]?.containerId;\n if (!targetContainerManifestHash || !targetContainerId) {\n throw new Error(\"Document create target is unavailable\");\n }\n\n const body: DocumentLinkAccessEventBody = {\n blobRewraps: [],\n eventType: \"document.link\",\n containerId: targetContainerId,\n containerManifestHash: targetContainerManifestHash,\n };\n const bodyHash = await computeAccessEventBodyHash(\n readCanonicalJson(body, \"Document create body\"),\n );\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId,\n eventType: \"document.link\",\n objectKind: \"document\",\n objectId: documentId,\n organizationId,\n previousManifestHash: null,\n dependencyManifestHashes: uniqueSortedStrings(\n containerProjection.path.map((bundle) => bundle.manifestHash),\n ),\n bodyHash,\n signerUserId: author.signerUserId,\n signerDeviceId: author.signerDeviceId,\n signerKeyFingerprint: author.signerKeyFingerprint,\n signedAt,\n };\n const event = await signAccessEvent(unsignedEvent, author.signerPrivateKey);\n const eventHash = await computeAccessEventHash(event);\n const state: DocumentLinkSetManifestState = {\n version: 1,\n documentId,\n organizationId,\n epoch: 1,\n previousManifestHash: null,\n eventHash,\n linkedContainerIds: [targetContainerId],\n };\n const manifest = await deriveDocumentLinkSetManifest(state);\n const manifestHash = await computeAccessManifestHash(manifest);\n const targetHash = await computeDocumentContentKeyTargetHash(targets);\n\n return {\n body,\n documentId,\n event,\n eventHash,\n manifest,\n manifestHash,\n request: {\n event: readCanonicalRecord(event, \"Document create event\"),\n body: readCanonicalRecord(body, \"Document create body\"),\n expectedManifestHash: manifestHash,\n manifest: readCanonicalRecord(manifest, \"Document create manifest\"),\n targetContainerPathRefs: containerPathRefs(containerProjection.path),\n contentKeyBundle: {\n contentKeyEpoch,\n linkSetManifestHash: manifestHash,\n targetHash,\n targets: targetEnvelopesForRequest,\n },\n },\n state,\n targetHash,\n targets: sortDocumentTargets(targets),\n };\n}\n\nexport async function buildDocumentLinkSetEventPlan(input: {\n blobRewraps: DocumentLinkAccessEventBody[\"blobRewraps\"];\n author: DocumentCreateAuthor;\n eventId: string;\n operation: DocumentLinkSetMutationOperation;\n // The document's existing organization (from its manifest). Link/unlink does\n // not change ownership, so the event keeps the document's org, which is the\n // container's org — never the acting member's personal org.\n organizationId: string;\n signedAt: string;\n targetContainerPathRefs: readonly ContainerManifestRef[];\n targetState: DocumentLinkSetTargetState;\n writerProjection: DocumentWriterProjectionResponse;\n}): Promise<DocumentLinkSetEventPlan> {\n const eventType =\n input.operation === \"link\" ? \"document.link\" : \"document.unlink\";\n const body: DocumentLinkSetMutationBody = {\n blobRewraps: input.blobRewraps,\n eventType,\n containerId: input.targetState.target.containerId,\n containerManifestHash: input.targetState.target.containerManifestHash,\n };\n const bodyHash = await computeAccessEventBodyHash(\n readCanonicalJson(body, \"Document link-set body\"),\n );\n const authorizingContainerPathRefs = authorizingContainerPathRefsForLinkSet({\n operation: input.operation,\n targetContainerId: input.targetState.target.containerId,\n writerProjection: input.writerProjection,\n });\n const dependencyManifestHashes = uniqueSortedStrings(\n [\n ...input.targetContainerPathRefs,\n ...authorizingContainerPathRefs.flat(),\n ].map((ref) => ref.manifestHash),\n );\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId: input.eventId,\n eventType,\n objectKind: \"document\",\n objectId: input.writerProjection.documentId,\n organizationId: input.organizationId,\n previousManifestHash: input.writerProjection.documentManifest.manifestHash,\n dependencyManifestHashes,\n bodyHash,\n signerUserId: input.author.signerUserId,\n signerDeviceId: input.author.signerDeviceId,\n signerKeyFingerprint: input.author.signerKeyFingerprint,\n signedAt: input.signedAt,\n };\n const event = await signAccessEvent(\n unsignedEvent,\n input.author.signerPrivateKey,\n );\n const eventHash = await computeAccessEventHash(event);\n\n return {\n authorizingContainerPathRefs,\n body,\n event,\n eventHash,\n };\n}\n"]}
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["events.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n computeAccessEventBodyHash,\n computeAccessEventHash,\n computeAccessManifestHash,\n computeDocumentContentKeyTargetHash,\n type DocumentLinkAccessEventBody,\n type DocumentLinkSetManifestState,\n deriveDocumentLinkSetManifest,\n readKeyingCanonicalJson,\n signAccessEvent,\n type UnsignedAccessEvent,\n} from \"@tearleads/crypto\";\nimport type { ContainerManifestRef } from \"@tearleads/validators/request\";\nimport type { DocumentWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport { readCanonicalRecord } from \"../../keying/canonicalJson\";\nimport {\n authorizingContainerPathRefsForLinkSet,\n containerPathRefs,\n deriveDocumentCreateTargets,\n mergeTargetEnvelopes,\n} from \"./projection\";\nimport { sortDocumentTargets, uniqueSortedStrings } from \"./readers\";\nimport type {\n BuildDocumentCreatePlanInput,\n DocumentCreateAuthor,\n DocumentCreatePlan,\n DocumentLinkSetEventPlan,\n DocumentLinkSetMutationBody,\n DocumentLinkSetMutationOperation,\n DocumentLinkSetTargetState,\n} from \"./types\";\n\nexport async function buildDocumentCreatePlan({\n author,\n containerProjection,\n contentKeyEpoch = 1,\n documentId = crypto.randomUUID(),\n eventId = crypto.randomUUID(),\n signedAt = new Date().toISOString(),\n targetEnvelopes,\n}: BuildDocumentCreatePlanInput): Promise<DocumentCreatePlan> {\n // The document belongs to its container's organization, not the author's. A\n // member authoring a document under another org's shared container creates a\n // document owned by that org; the author supplies only the signer identity.\n const organizationId = containerProjection.organizationId;\n\n const targets = deriveDocumentCreateTargets(containerProjection);\n const targetEnvelopesForRequest = mergeTargetEnvelopes(\n targets,\n targetEnvelopes,\n );\n const targetContainerManifestHash = targets[0]?.containerManifestHash;\n const targetContainerId = targets[0]?.containerId;\n if (!targetContainerManifestHash || !targetContainerId) {\n throw new Error(\"Document create target is unavailable\");\n }\n\n const body: DocumentLinkAccessEventBody = {\n blobRewraps: [],\n eventType: \"document.link\",\n containerId: targetContainerId,\n containerManifestHash: targetContainerManifestHash,\n };\n const bodyHash = await computeAccessEventBodyHash(\n readKeyingCanonicalJson(body, \"Document create body\"),\n );\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId,\n eventType: \"document.link\",\n objectKind: \"document\",\n objectId: documentId,\n organizationId,\n previousManifestHash: null,\n dependencyManifestHashes: uniqueSortedStrings(\n containerProjection.path.map((bundle) => bundle.manifestHash),\n ),\n bodyHash,\n signerUserId: author.signerUserId,\n signerDeviceId: author.signerDeviceId,\n signerKeyFingerprint: author.signerKeyFingerprint,\n signedAt,\n };\n const event = await signAccessEvent(unsignedEvent, author.signerPrivateKey);\n const eventHash = await computeAccessEventHash(event);\n const state: DocumentLinkSetManifestState = {\n version: 1,\n documentId,\n organizationId,\n epoch: 1,\n previousManifestHash: null,\n eventHash,\n linkedContainerIds: [targetContainerId],\n };\n const manifest = await deriveDocumentLinkSetManifest(state);\n const manifestHash = await computeAccessManifestHash(manifest);\n const targetHash = await computeDocumentContentKeyTargetHash(targets);\n\n return {\n body,\n documentId,\n event,\n eventHash,\n manifest,\n manifestHash,\n request: {\n event: readCanonicalRecord(event, \"Document create event\"),\n body: readCanonicalRecord(body, \"Document create body\"),\n expectedManifestHash: manifestHash,\n manifest: readCanonicalRecord(manifest, \"Document create manifest\"),\n targetContainerPathRefs: containerPathRefs(containerProjection.path),\n contentKeyBundle: {\n contentKeyEpoch,\n linkSetManifestHash: manifestHash,\n targetHash,\n targets: targetEnvelopesForRequest,\n },\n },\n state,\n targetHash,\n targets: sortDocumentTargets(targets),\n };\n}\n\nexport async function buildDocumentLinkSetEventPlan(input: {\n blobRewraps: DocumentLinkAccessEventBody[\"blobRewraps\"];\n author: DocumentCreateAuthor;\n eventId: string;\n operation: DocumentLinkSetMutationOperation;\n // The document's existing organization (from its manifest). Link/unlink does\n // not change ownership, so the event keeps the document's org, which is the\n // container's org — never the acting member's personal org.\n organizationId: string;\n signedAt: string;\n targetContainerPathRefs: readonly ContainerManifestRef[];\n targetState: DocumentLinkSetTargetState;\n writerProjection: DocumentWriterProjectionResponse;\n}): Promise<DocumentLinkSetEventPlan> {\n const eventType =\n input.operation === \"link\" ? \"document.link\" : \"document.unlink\";\n const body: DocumentLinkSetMutationBody = {\n blobRewraps: input.blobRewraps,\n eventType,\n containerId: input.targetState.target.containerId,\n containerManifestHash: input.targetState.target.containerManifestHash,\n };\n const bodyHash = await computeAccessEventBodyHash(\n readKeyingCanonicalJson(body, \"Document link-set body\"),\n );\n const authorizingContainerPathRefs = authorizingContainerPathRefsForLinkSet({\n operation: input.operation,\n targetContainerId: input.targetState.target.containerId,\n writerProjection: input.writerProjection,\n });\n const dependencyManifestHashes = uniqueSortedStrings(\n [\n ...input.targetContainerPathRefs,\n ...authorizingContainerPathRefs.flat(),\n ].map((ref) => ref.manifestHash),\n );\n const unsignedEvent: UnsignedAccessEvent = {\n version: 1,\n eventId: input.eventId,\n eventType,\n objectKind: \"document\",\n objectId: input.writerProjection.documentId,\n organizationId: input.organizationId,\n previousManifestHash: input.writerProjection.documentManifest.manifestHash,\n dependencyManifestHashes,\n bodyHash,\n signerUserId: input.author.signerUserId,\n signerDeviceId: input.author.signerDeviceId,\n signerKeyFingerprint: input.author.signerKeyFingerprint,\n signedAt: input.signedAt,\n };\n const event = await signAccessEvent(\n unsignedEvent,\n input.author.signerPrivateKey,\n );\n const eventHash = await computeAccessEventHash(event);\n\n return {\n authorizingContainerPathRefs,\n body,\n event,\n eventHash,\n };\n}\n"]}
@@ -1,72 +1,10 @@
1
- import { serializeKeyingCanonicalJson } from "../../internal/crypto/index.js";
1
+ import { readKeyingCanonicalJson, serializeKeyingCanonicalJson, } from "../../internal/crypto/index.js";
2
2
  import { isPlainObject } from "../../internal/validators/isPlainObject.js";
3
- function isCanonicalJsonScalar(value) {
4
- return (value === null ||
5
- typeof value === "string" ||
6
- typeof value === "boolean" ||
7
- (typeof value === "number" && Number.isFinite(value)));
8
- }
9
- function pushCanonicalJsonChildren(value, active, pending) {
10
- if (Array.isArray(value)) {
11
- if (active.has(value)) {
12
- return false;
13
- }
14
- active.add(value);
15
- pending.push({ leave: value });
16
- for (let index = 0; index < value.length; index += 1) {
17
- if (!(index in value)) {
18
- return false;
19
- }
20
- pending.push({ value: value[index] });
21
- }
22
- return true;
23
- }
24
- if (!isPlainObject(value)) {
25
- return false;
26
- }
27
- if (active.has(value)) {
28
- return false;
29
- }
30
- active.add(value);
31
- pending.push({ leave: value });
32
- for (const key of Object.keys(value)) {
33
- pending.push({ value: Reflect.get(value, key) });
34
- }
35
- return true;
36
- }
37
- function isCanonicalJson(value) {
38
- const pending = [{ value }];
39
- const active = new WeakSet();
40
- while (pending.length > 0) {
41
- const frame = pending.pop();
42
- if (!frame) {
43
- break;
44
- }
45
- if (frame.leave) {
46
- active.delete(frame.leave);
47
- continue;
48
- }
49
- const item = frame.value;
50
- if (isCanonicalJsonScalar(item)) {
51
- continue;
52
- }
53
- if (!pushCanonicalJsonChildren(item, active, pending)) {
54
- return false;
55
- }
56
- }
57
- return true;
58
- }
59
- export function readCanonicalJson(value, label) {
60
- if (!isCanonicalJson(value)) {
61
- throw new Error(`${label} must be canonical JSON`);
62
- }
63
- return value;
64
- }
65
3
  export function readCanonicalRecord(value, label) {
66
4
  if (!isPlainObject(value)) {
67
5
  throw new Error(`${label} must be an object`);
68
6
  }
69
- readCanonicalJson(value, label);
7
+ readKeyingCanonicalJson(value, label);
70
8
  return value;
71
9
  }
72
10
  export function readCanonicalRecords(values, label) {
@@ -78,6 +16,6 @@ export function readCanonicalRecords(values, label) {
78
16
  * invited the composition itself to drift.
79
17
  */
80
18
  export function canonicalKeyingJsonString(value, label) {
81
- return serializeKeyingCanonicalJson(readCanonicalJson(value, label));
19
+ return serializeKeyingCanonicalJson(readKeyingCanonicalJson(value, label));
82
20
  }
83
21
  //# sourceMappingURL=canonicalJson.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"canonicalJson.js","sourceRoot":"","sources":["canonicalJson.ts"],"names":[],"mappings":"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","sourcesContent":["import type { KeyingCanonicalJson } from \"@tearleads/crypto\";\nimport { serializeKeyingCanonicalJson } from \"@tearleads/crypto\";\nimport { isPlainObject } from \"@tearleads/validators/isPlainObject\";\n\ninterface CanonicalJsonFrame {\n leave?: object;\n value?: unknown;\n}\n\nfunction isCanonicalJsonScalar(value: unknown): boolean {\n return (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"boolean\" ||\n (typeof value === \"number\" && Number.isFinite(value))\n );\n}\n\nfunction pushCanonicalJsonChildren(\n value: unknown,\n active: WeakSet<object>,\n pending: CanonicalJsonFrame[],\n): boolean {\n if (Array.isArray(value)) {\n if (active.has(value)) {\n return false;\n }\n active.add(value);\n pending.push({ leave: value });\n for (let index = 0; index < value.length; index += 1) {\n if (!(index in value)) {\n return false;\n }\n pending.push({ value: value[index] });\n }\n return true;\n }\n\n if (!isPlainObject(value)) {\n return false;\n }\n if (active.has(value)) {\n return false;\n }\n active.add(value);\n pending.push({ leave: value });\n for (const key of Object.keys(value)) {\n pending.push({ value: Reflect.get(value, key) });\n }\n return true;\n}\n\nfunction isCanonicalJson(value: unknown): value is KeyingCanonicalJson {\n const pending: CanonicalJsonFrame[] = [{ value }];\n const active = new WeakSet<object>();\n\n while (pending.length > 0) {\n const frame = pending.pop();\n if (!frame) {\n break;\n }\n if (frame.leave) {\n active.delete(frame.leave);\n continue;\n }\n\n const item = frame.value;\n if (isCanonicalJsonScalar(item)) {\n continue;\n }\n if (!pushCanonicalJsonChildren(item, active, pending)) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function readCanonicalJson(\n value: unknown,\n label: string,\n): KeyingCanonicalJson {\n if (!isCanonicalJson(value)) {\n throw new Error(`${label} must be canonical JSON`);\n }\n return value;\n}\n\nexport function readCanonicalRecord(\n value: unknown,\n label: string,\n): Record<string, unknown> {\n if (!isPlainObject(value)) {\n throw new Error(`${label} must be an object`);\n }\n readCanonicalJson(value, label);\n return value;\n}\n\nexport function readCanonicalRecords(\n values: readonly unknown[],\n label: string,\n): Record<string, unknown>[] {\n return values.map((value, index) =>\n readCanonicalRecord(value, `${label}[${index}]`),\n );\n}\n\n/**\n * Canonicalize-then-serialize, the one string form every keying equality\n * check compares. Both halves are shared already; composing them per-file\n * invited the composition itself to drift.\n */\nexport function canonicalKeyingJsonString(\n value: unknown,\n label: string,\n): string {\n return serializeKeyingCanonicalJson(readCanonicalJson(value, label));\n}\n"]}
1
+ {"version":3,"file":"canonicalJson.js","sourceRoot":"","sources":["canonicalJson.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAEpE,MAAM,UAAU,mBAAmB,CACjC,KAAc,EACd,KAAa;IAEb,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,oBAAoB,CAAC,CAAC;IAChD,CAAC;IACD,uBAAuB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,MAA0B,EAC1B,KAAa;IAEb,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CACjC,mBAAmB,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC,CACjD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,KAAc,EACd,KAAa;IAEb,OAAO,4BAA4B,CAAC,uBAAuB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AAC7E,CAAC","sourcesContent":["import {\n readKeyingCanonicalJson,\n serializeKeyingCanonicalJson,\n} from \"@tearleads/crypto\";\nimport { isPlainObject } from \"@tearleads/validators/isPlainObject\";\n\nexport function readCanonicalRecord(\n value: unknown,\n label: string,\n): Record<string, unknown> {\n if (!isPlainObject(value)) {\n throw new Error(`${label} must be an object`);\n }\n readKeyingCanonicalJson(value, label);\n return value;\n}\n\nexport function readCanonicalRecords(\n values: readonly unknown[],\n label: string,\n): Record<string, unknown>[] {\n return values.map((value, index) =>\n readCanonicalRecord(value, `${label}[${index}]`),\n );\n}\n\n/**\n * Canonicalize-then-serialize, the one string form every keying equality\n * check compares. Both halves are shared already; composing them per-file\n * invited the composition itself to drift.\n */\nexport function canonicalKeyingJsonString(\n value: unknown,\n label: string,\n): string {\n return serializeKeyingCanonicalJson(readKeyingCanonicalJson(value, label));\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { KeyingVerificationError, verifySignedAccessEvent, } from "../../../internal/crypto/index.js";
2
- import { canonicalKeyingJsonString, readCanonicalJson, readCanonicalRecord, } from "../../keying/canonicalJson.js";
1
+ import { KeyingVerificationError, readKeyingCanonicalJson, verifySignedAccessEvent, } from "../../../internal/crypto/index.js";
2
+ import { canonicalKeyingJsonString, readCanonicalRecord, } from "../../keying/canonicalJson.js";
3
3
  import { ProjectionDependencyUnavailableError } from "../dependencyUnavailable.js";
4
4
  import { readKeyingVerificationShape } from "../error.js";
5
5
  import { readAccessEvent, readRecordString, readRequiredRecordValue, } from "../readers.js";
@@ -28,7 +28,7 @@ function readStandaloneEventBundle(bundle, label) {
28
28
  const eventBundle = readCanonicalRecord(bundle, `${label} event bundle`);
29
29
  const eventHash = readRecordString(eventBundle, "eventHash", `${label} event bundle`);
30
30
  const event = readAccessEvent(readRequiredRecordValue(eventBundle, "event", `${label} event bundle`), `${label} signed event`);
31
- const body = readCanonicalJson(readRequiredRecordValue(eventBundle, "body", `${label} event bundle`), `${label} event body`);
31
+ const body = readKeyingCanonicalJson(readRequiredRecordValue(eventBundle, "body", `${label} event bundle`), `${label} event body`);
32
32
  return { eventHash, event, body };
33
33
  });
34
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bundleVerification.js","sourceRoot":"","sources":["bundleVerification.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n verifySignedAccessEvent,\n} from \"@tearleads/crypto\";\nimport type { AccessManifestBundleWireResponse } from \"@tearleads/validators/response\";\nimport type { AccessEventBundleWireResponse } from \"@tearleads/validators/util\";\nimport {\n canonicalKeyingJsonString,\n readCanonicalJson,\n readCanonicalRecord,\n} from \"../../keying/canonicalJson\";\nimport { ProjectionDependencyUnavailableError } from \"../dependencyUnavailable\";\nimport { readKeyingVerificationShape } from \"../error\";\nimport {\n readAccessEvent,\n readRecordString,\n readRequiredRecordValue,\n} from \"../readers\";\nimport type { ProjectionUserKeyResolver } from \"../types\";\n\nfunction canonicalBundleJson(value: unknown, label: string): string {\n return readKeyingVerificationShape(() =>\n canonicalKeyingJsonString(value, label),\n );\n}\n\nexport function assertCanonicalEqual(input: {\n readonly actual: unknown;\n readonly expected: unknown;\n readonly label: string;\n}): void {\n if (\n canonicalBundleJson(input.actual, `${input.label} actual`) !==\n canonicalBundleJson(input.expected, `${input.label} expected`)\n ) {\n throw new KeyingVerificationError(\n \"hash_mismatch\",\n `${input.label} mismatch`,\n );\n }\n}\n\nexport function addBundleByHash(\n bundlesByHash: Map<string, AccessManifestBundleWireResponse>,\n bundle: AccessManifestBundleWireResponse,\n label: string,\n): void {\n const existing = bundlesByHash.get(bundle.manifestHash);\n if (!existing) {\n bundlesByHash.set(bundle.manifestHash, bundle);\n return;\n }\n\n if (\n canonicalBundleJson(existing, `${label} existing`) !==\n canonicalBundleJson(bundle, `${label} duplicate`)\n ) {\n throw new KeyingVerificationError(\n \"hash_mismatch\",\n `Writer projection has equivocal manifest bundle ${bundle.manifestHash}`,\n );\n }\n}\n\nfunction readStandaloneEventBundle(\n bundle: AccessEventBundleWireResponse,\n label: string,\n) {\n return readKeyingVerificationShape(() => {\n const eventBundle = readCanonicalRecord(bundle, `${label} event bundle`);\n const eventHash = readRecordString(\n eventBundle,\n \"eventHash\",\n `${label} event bundle`,\n );\n const event = readAccessEvent(\n readRequiredRecordValue(eventBundle, \"event\", `${label} event bundle`),\n `${label} signed event`,\n );\n const body = readCanonicalJson(\n readRequiredRecordValue(eventBundle, \"body\", `${label} event bundle`),\n `${label} event body`,\n );\n return { eventHash, event, body };\n });\n}\n\nexport async function verifyStandaloneAccessEventBundle(input: {\n readonly bundle: AccessEventBundleWireResponse;\n readonly label: string;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n}) {\n const { eventHash, event, body } = readStandaloneEventBundle(\n input.bundle,\n input.label,\n );\n const userKey = await input.resolveUserKey(event.signerUserId);\n if (!userKey) {\n throw new ProjectionDependencyUnavailableError(\n `${input.label} signer public key could not be resolved for ${event.signerUserId}`,\n );\n }\n\n const verified = await verifySignedAccessEvent({\n body,\n event,\n signerPublicKey: userKey.signingPublicKey,\n });\n if (!verified.ok) {\n throw verified.error;\n }\n if (verified.value.eventHash !== eventHash) {\n throw new KeyingVerificationError(\n \"hash_mismatch\",\n `${input.label} event hash mismatch`,\n );\n }\n\n return verified.value;\n}\n\nexport function verifyAccessEventBundle(input: {\n readonly bundle: AccessManifestBundleWireResponse;\n readonly label: string;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n}) {\n return verifyStandaloneAccessEventBundle({\n bundle: input.bundle.event,\n label: input.label,\n resolveUserKey: input.resolveUserKey,\n });\n}\n"]}
1
+ {"version":3,"file":"bundleVerification.js","sourceRoot":"","sources":["bundleVerification.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n readKeyingCanonicalJson,\n verifySignedAccessEvent,\n} from \"@tearleads/crypto\";\nimport type { AccessManifestBundleWireResponse } from \"@tearleads/validators/response\";\nimport type { AccessEventBundleWireResponse } from \"@tearleads/validators/util\";\nimport {\n canonicalKeyingJsonString,\n readCanonicalRecord,\n} from \"../../keying/canonicalJson\";\nimport { ProjectionDependencyUnavailableError } from \"../dependencyUnavailable\";\nimport { readKeyingVerificationShape } from \"../error\";\nimport {\n readAccessEvent,\n readRecordString,\n readRequiredRecordValue,\n} from \"../readers\";\nimport type { ProjectionUserKeyResolver } from \"../types\";\n\nfunction canonicalBundleJson(value: unknown, label: string): string {\n return readKeyingVerificationShape(() =>\n canonicalKeyingJsonString(value, label),\n );\n}\n\nexport function assertCanonicalEqual(input: {\n readonly actual: unknown;\n readonly expected: unknown;\n readonly label: string;\n}): void {\n if (\n canonicalBundleJson(input.actual, `${input.label} actual`) !==\n canonicalBundleJson(input.expected, `${input.label} expected`)\n ) {\n throw new KeyingVerificationError(\n \"hash_mismatch\",\n `${input.label} mismatch`,\n );\n }\n}\n\nexport function addBundleByHash(\n bundlesByHash: Map<string, AccessManifestBundleWireResponse>,\n bundle: AccessManifestBundleWireResponse,\n label: string,\n): void {\n const existing = bundlesByHash.get(bundle.manifestHash);\n if (!existing) {\n bundlesByHash.set(bundle.manifestHash, bundle);\n return;\n }\n\n if (\n canonicalBundleJson(existing, `${label} existing`) !==\n canonicalBundleJson(bundle, `${label} duplicate`)\n ) {\n throw new KeyingVerificationError(\n \"hash_mismatch\",\n `Writer projection has equivocal manifest bundle ${bundle.manifestHash}`,\n );\n }\n}\n\nfunction readStandaloneEventBundle(\n bundle: AccessEventBundleWireResponse,\n label: string,\n) {\n return readKeyingVerificationShape(() => {\n const eventBundle = readCanonicalRecord(bundle, `${label} event bundle`);\n const eventHash = readRecordString(\n eventBundle,\n \"eventHash\",\n `${label} event bundle`,\n );\n const event = readAccessEvent(\n readRequiredRecordValue(eventBundle, \"event\", `${label} event bundle`),\n `${label} signed event`,\n );\n const body = readKeyingCanonicalJson(\n readRequiredRecordValue(eventBundle, \"body\", `${label} event bundle`),\n `${label} event body`,\n );\n return { eventHash, event, body };\n });\n}\n\nexport async function verifyStandaloneAccessEventBundle(input: {\n readonly bundle: AccessEventBundleWireResponse;\n readonly label: string;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n}) {\n const { eventHash, event, body } = readStandaloneEventBundle(\n input.bundle,\n input.label,\n );\n const userKey = await input.resolveUserKey(event.signerUserId);\n if (!userKey) {\n throw new ProjectionDependencyUnavailableError(\n `${input.label} signer public key could not be resolved for ${event.signerUserId}`,\n );\n }\n\n const verified = await verifySignedAccessEvent({\n body,\n event,\n signerPublicKey: userKey.signingPublicKey,\n });\n if (!verified.ok) {\n throw verified.error;\n }\n if (verified.value.eventHash !== eventHash) {\n throw new KeyingVerificationError(\n \"hash_mismatch\",\n `${input.label} event hash mismatch`,\n );\n }\n\n return verified.value;\n}\n\nexport function verifyAccessEventBundle(input: {\n readonly bundle: AccessManifestBundleWireResponse;\n readonly label: string;\n readonly resolveUserKey: ProjectionUserKeyResolver;\n}) {\n return verifyStandaloneAccessEventBundle({\n bundle: input.bundle.event,\n label: input.label,\n resolveUserKey: input.resolveUserKey,\n });\n}\n"]}
@@ -1,5 +1,4 @@
1
- import { serializeKeyingCanonicalJson } from "../../../internal/crypto/index.js";
2
- import { readCanonicalJson } from "../../keying/canonicalJson.js";
1
+ import { readKeyingCanonicalJson, serializeKeyingCanonicalJson, } from "../../../internal/crypto/index.js";
3
2
  // The exact bytes each manifest was verified from, keyed by the verified
4
3
  // result. A later projection can hit the same verified hash while serving
5
4
  // different bytes; only bytes that were themselves verified may be stored
@@ -7,7 +6,7 @@ import { readCanonicalJson } from "../../keying/canonicalJson.js";
7
6
  const verifiedBundleBytes = new WeakMap();
8
7
  function canonicalBytes(bundle) {
9
8
  try {
10
- return serializeKeyingCanonicalJson(readCanonicalJson(bundle, "Verified manifest bundle"));
9
+ return serializeKeyingCanonicalJson(readKeyingCanonicalJson(bundle, "Verified manifest bundle"));
11
10
  }
12
11
  catch {
13
12
  return null;
@@ -1 +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"]}
1
+ {"version":3,"file":"verifiedBundleBytes.js","sourceRoot":"","sources":["verifiedBundleBytes.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC;AAE3B,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,uBAAuB,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAC5D,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 {\n readKeyingCanonicalJson,\n serializeKeyingCanonicalJson,\n} from \"@tearleads/crypto\";\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 readKeyingCanonicalJson(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"]}
@@ -1,5 +1,5 @@
1
- import { KeyingVerificationError, principalPolicyMatchesReference, verifyContainerAccessManifest, } from "../../../internal/crypto/index.js";
2
- import { readCanonicalJson, readCanonicalRecord, } from "../../keying/canonicalJson.js";
1
+ import { KeyingVerificationError, principalPolicyMatchesReference, readKeyingCanonicalJson, verifyContainerAccessManifest, } from "../../../internal/crypto/index.js";
2
+ import { readCanonicalRecord } from "../../keying/canonicalJson.js";
3
3
  import { assertCanonicalEqual, verifyAccessEventBundle, } from "../checkpoints/bundleVerification.js";
4
4
  import { loadManifestCheckpointVerification, verifyCachedManifestCheckpoint, } from "../checkpoints/manifestCheckpointVerification.js";
5
5
  import { recordVerifiedBundleBytes } from "../checkpoints/verifiedBundleBytes.js";
@@ -106,7 +106,7 @@ async function verifyFreshContainerManifestBundle(input, parentPath) {
106
106
  }
107
107
  assertCanonicalEqual({
108
108
  actual: input.bundle.state,
109
- expected: readCanonicalJson(verified.value.state, `${input.label} state`),
109
+ expected: readKeyingCanonicalJson(verified.value.state, `${input.label} state`),
110
110
  label: `${input.label} state`,
111
111
  });
112
112
  input.verifiedByHash.set(input.bundle.manifestHash, verified.value);