@tearleads/client-sdk 0.1.117 → 0.1.118

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 (65) hide show
  1. package/data/keyingProjectionVerification/checkpoints/checkpointContext.d.ts +8 -1
  2. package/data/keyingProjectionVerification/checkpoints/checkpointContext.js +15 -0
  3. package/data/keyingProjectionVerification/checkpoints/checkpointContext.js.map +1 -1
  4. package/data/keyingProjectionVerification/container/containerProjectionVerification.js +3 -1
  5. package/data/keyingProjectionVerification/container/containerProjectionVerification.js.map +1 -1
  6. package/data/keyingProjectionVerification/document/documentProjectionVerification.js +2 -1
  7. package/data/keyingProjectionVerification/document/documentProjectionVerification.js.map +1 -1
  8. package/data/persistence/verifiedCitations/citationScope.js +121 -0
  9. package/data/persistence/verifiedCitations/citationScope.js.map +1 -0
  10. package/data/persistence/verifiedCitations/kekLevelCitations.d.ts +33 -0
  11. package/data/persistence/verifiedCitations/kekLevelCitations.js +44 -0
  12. package/data/persistence/verifiedCitations/kekLevelCitations.js.map +1 -0
  13. package/data/persistence/verifiedManifestBundlePersistence.d.ts +17 -12
  14. package/data/persistence/verifiedManifestBundlePersistence.js +75 -104
  15. package/data/persistence/verifiedManifestBundlePersistence.js.map +1 -1
  16. package/data/sqlite/schema/verifiedManifestBundleSchema.d.ts +7 -2
  17. package/data/sqlite/schema/verifiedManifestBundleSchema.js +7 -2
  18. package/data/sqlite/schema/verifiedManifestBundleSchema.js.map +1 -1
  19. package/internal/api-client/ApiClient.d.ts +3 -2
  20. package/internal/api-client/ApiClient.js +3 -2
  21. package/internal/api-client/ApiClient.js.map +1 -1
  22. package/internal/api-client/index.d.ts +1 -1
  23. package/internal/api-client/index.js.map +1 -1
  24. package/internal/api-client/types.d.ts +7 -0
  25. package/internal/api-client/types.js.map +1 -1
  26. package/internal/api-client/verifiedManifestCitations.d.ts +26 -9
  27. package/internal/api-client/verifiedManifestCitations.js +44 -25
  28. package/internal/api-client/verifiedManifestCitations.js.map +1 -1
  29. package/internal/api-client/writerProjectionRequests.js +21 -10
  30. package/internal/api-client/writerProjectionRequests.js.map +1 -1
  31. package/internal/crypto/index.d.ts +1 -1
  32. package/internal/crypto/index.js +1 -1
  33. package/internal/crypto/index.js.map +1 -1
  34. package/internal/crypto/keying/canonicalJsonGuard.d.ts +7 -0
  35. package/internal/crypto/keying/canonicalJsonGuard.js +20 -0
  36. package/internal/crypto/keying/canonicalJsonGuard.js.map +1 -1
  37. package/internal/crypto/keying/index.d.ts +1 -1
  38. package/internal/crypto/keying/index.js +1 -1
  39. package/internal/crypto/keying/index.js.map +1 -1
  40. package/internal/validators/knownManifestsRefinements.js +1 -1
  41. package/internal/validators/knownManifestsRefinements.js.map +1 -1
  42. package/internal/validators/operation/writerProjections.d.ts +42 -18
  43. package/internal/validators/response/container.d.ts +137 -16
  44. package/internal/validators/response/container.js +23 -2
  45. package/internal/validators/response/container.js.map +1 -1
  46. package/internal/validators/response/documentMutation.d.ts +20 -8
  47. package/internal/validators/response/index.d.ts +2 -2
  48. package/internal/validators/response/index.js +2 -2
  49. package/internal/validators/response/index.js.map +1 -1
  50. package/internal/validators/response/writerProjectionWire.d.ts +19 -8
  51. package/internal/validators/response/writerProjectionWire.js +78 -20
  52. package/internal/validators/response/writerProjectionWire.js.map +1 -1
  53. package/internal/validators/util/index.d.ts +1 -1
  54. package/internal/validators/util/index.js +1 -1
  55. package/internal/validators/util/index.js.map +1 -1
  56. package/internal/validators/util/knownManifestWire.d.ts +13 -3
  57. package/internal/validators/util/knownManifestWire.js +37 -10
  58. package/internal/validators/util/knownManifestWire.js.map +1 -1
  59. package/package.json +1 -1
  60. package/workflows/container-contents/remoteHydration/destination/destinationPrefetch.js +10 -5
  61. package/workflows/container-contents/remoteHydration/destination/destinationPrefetch.js.map +1 -1
  62. package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerPageHydration.js +4 -6
  63. package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerPageHydration.js.map +1 -1
  64. package/workflows/container-contents/remoteHydration/types.d.ts +2 -2
  65. package/workflows/container-contents/remoteHydration/types.js.map +1 -1
@@ -1,14 +1,15 @@
1
- import type { VerifiedManifestCitations, WriterProjectionTarget } from "../../internal/api-client/index.js";
1
+ import type { HeldProjectionCitation, VerifiedManifestCitations, WriterProjectionTarget } from "../../internal/api-client/index.js";
2
2
  import { type AccessManifestBundleWireResponse } from "../../internal/validators/response/index.js";
3
3
  import { verifiedAccessManifestBundles as bundles } from "../sqlite/schema/verifiedManifestBundleSchema.js";
4
4
  import { type ClientSQLiteTransactionScope } from "../sqlite/sqlitePersistenceRuntime.js";
5
5
  import { type ExecSql } from "../sqlite/sqlSchema.js";
6
+ import { type VerifiedKekLevel } from "./verifiedCitations/kekLevelCitations.js";
6
7
  /**
7
8
  * Bounds local bytes (a bundle is ~10–20 KB). Evicting the least recently
8
9
  * verified rows only makes a later read carry those bundles again.
9
10
  */
10
11
  export declare const MAX_VERIFIED_MANIFEST_BUNDLES = 1024;
11
- export interface VerifiedManifestBundle {
12
+ interface VerifiedBundle {
12
13
  readonly bundle: AccessManifestBundleWireResponse;
13
14
  readonly checkpoint: {
14
15
  readonly objectKind: string;
@@ -17,12 +18,14 @@ export interface VerifiedManifestBundle {
17
18
  readonly epoch: number;
18
19
  };
19
20
  }
21
+ /** Bytes a projection verification accepted: a bundle or a KEK level. */
22
+ export type VerifiedManifestBundle = VerifiedBundle | VerifiedKekLevel;
20
23
  /** A verified bundle's exact JSON and that JSON's digest. */
21
24
  export type VerifiedManifestBundleRow = Omit<typeof bundles.$inferInsert, "verifiedAt">;
22
25
  /**
23
- * Prepares rows for bundles that just passed projection verification. The
24
- * digests are computed here, before the checkpoint transaction that writes
25
- * them begins.
26
+ * Prepares rows for bundles and KEK levels that just passed projection
27
+ * verification. The digests are computed here, before the checkpoint
28
+ * transaction that writes them begins.
26
29
  */
27
30
  export declare function verifiedManifestBundleRows(entries: readonly VerifiedManifestBundle[]): Promise<VerifiedManifestBundleRow[]>;
28
31
  /**
@@ -33,15 +36,17 @@ export declare function verifiedManifestBundleRows(entries: readonly VerifiedMan
33
36
  */
34
37
  export declare function writeVerifiedManifestBundlesInTransaction(tx: ClientSQLiteTransactionScope, rows: readonly VerifiedManifestBundleRow[], verifiedAt: string): Promise<void>;
35
38
  /**
36
- * Verified bundles of the target's object and of the containers its
37
- * projection walks, newest epochs first; then the most recently verified rows
38
- * of its organization, since a just-verified parent is the likeliest part of
39
- * a new child's projection. Citations never leave the target's organization:
40
- * an object whose organization the device cannot place cites nothing.
41
- * Damaged rows are dropped.
39
+ * The latest verified KEK level of each container the target's projection
40
+ * walks; then verified bundles of those objects, newest epochs first; then
41
+ * the most recently verified bundles of its organization, since a
42
+ * just-verified parent is the likeliest part of a new child's projection.
43
+ * Citations never leave the target's organization: an object whose
44
+ * organization the device cannot place cites nothing. Damaged rows are
45
+ * dropped.
42
46
  */
43
- export declare function loadVerifiedManifestCitations(execSql: ExecSql, target: WriterProjectionTarget, limit?: number): Promise<AccessManifestBundleWireResponse[]>;
47
+ export declare function loadVerifiedManifestCitations(execSql: ExecSql, target: WriterProjectionTarget, limit?: number): Promise<HeldProjectionCitation[]>;
44
48
  /** Cites from whichever database the signed-in identity currently uses. */
45
49
  export declare function createVerifiedManifestCitations(resolveExecSql: () => ExecSql | null): VerifiedManifestCitations;
46
50
  /** Drops an organization's verified bundles when a remote reset forgets it. */
47
51
  export declare function deleteVerifiedManifestBundlesForOrganization(tx: ClientSQLiteTransactionScope, organizationId: string): Promise<void>;
52
+ export {};
@@ -1,15 +1,15 @@
1
1
  import { toFingerprint } from "../../internal/crypto/index.js";
2
2
  import { isAccessManifestBundleWireResponse, } from "../../internal/validators/response/index.js";
3
- import { KNOWN_MANIFEST_HINT_LIMIT, MAX_CONTAINER_PATH_LENGTH, } from "../../internal/validators/util/index.js";
4
- import { and, asc, desc, eq, inArray, sql } from "drizzle-orm";
5
- import { containerTables, documentContainerProjection, documentContainerProjectionTables, } from "../sqlite/schema/index.js";
6
- import { containers } from "../sqlite/schema/containerSchema.js";
7
- import { documentDiscoveryHeads, pendingDocumentDiscoveries, } from "../sqlite/schema/documentDiscoveryEvidenceSchema.js";
3
+ import { KNOWN_MANIFEST_HINT_LIMIT } from "../../internal/validators/util/index.js";
4
+ import { and, asc, desc, eq, inArray, ne, notInArray, sql } from "drizzle-orm";
5
+ import { containerTables, documentContainerProjectionTables, } from "../sqlite/schema/index.js";
8
6
  import { verifiedAccessManifestBundles as bundles, verifiedManifestBundleTables, } from "../sqlite/schema/verifiedManifestBundleSchema.js";
9
7
  import { sqliteInsertBatchSize } from "../sqlite/sqliteBindBudget.js";
10
8
  import { getClientSQLitePersistenceRuntime, } from "../sqlite/sqlitePersistenceRuntime.js";
11
9
  import { excludedValue } from "../sqlite/sqliteUpsert.js";
12
10
  import { ensureSqlTables } from "../sqlite/sqlSchema.js";
11
+ import { citedObjects, targetOrganization, } from "./verifiedCitations/citationScope.js";
12
+ import { KEK_LEVEL_OBJECT_KIND, kekLevelRow, readStoredKekLevel, } from "./verifiedCitations/kekLevelCitations.js";
13
13
  /**
14
14
  * Bounds local bytes (a bundle is ~10–20 KB). Evicting the least recently
15
15
  * verified rows only makes a later read carry those bundles again.
@@ -43,11 +43,29 @@ async function pruneInTransaction(tx) {
43
43
  await tx.delete(bundles).where(inArray(bundles.manifestHash, batch)).run();
44
44
  }
45
45
  /**
46
- * Prepares rows for bundles that just passed projection verification. The
47
- * digests are computed here, before the checkpoint transaction that writes
48
- * them begins.
46
+ * Prepares rows for bundles and KEK levels that just passed projection
47
+ * verification. The digests are computed here, before the checkpoint
48
+ * transaction that writes them begins.
49
49
  */
50
- export function verifiedManifestBundleRows(entries) {
50
+ export async function verifiedManifestBundleRows(entries) {
51
+ const bundleEntries = [];
52
+ const levelEntries = [];
53
+ for (const entry of entries)
54
+ if ("kekLevel" in entry)
55
+ levelEntries.push(entry);
56
+ else
57
+ bundleEntries.push(entry);
58
+ // One level per container: its newest epoch, the last one seen on a tie.
59
+ const levelRows = new Map();
60
+ for (const level of levelEntries) {
61
+ const row = await kekLevelRow(level);
62
+ const held = row && levelRows.get(row.objectId);
63
+ if (row && (!held || held.epoch <= row.epoch))
64
+ levelRows.set(row.objectId, row);
65
+ }
66
+ return [...(await bundleRows(bundleEntries)), ...levelRows.values()];
67
+ }
68
+ function bundleRows(entries) {
51
69
  const unique = new Map(entries.map((entry) => [entry.bundle.manifestHash, entry]));
52
70
  return Promise.all([...unique.values()].map(async ({ bundle, checkpoint }) => {
53
71
  const bundleJson = JSON.stringify(bundle);
@@ -101,8 +119,21 @@ export async function writeVerifiedManifestBundlesInTransaction(tx, rows, verifi
101
119
  },
102
120
  })
103
121
  .run();
122
+ await dropSupersededKekLevels(tx, rows);
104
123
  await pruneInTransaction(tx);
105
124
  }
125
+ /**
126
+ * A container keeps only its latest verified level: the API serves its
127
+ * current level, so an older one can never be cited usefully again.
128
+ */
129
+ async function dropSupersededKekLevels(tx, rows) {
130
+ const levels = rows.filter((row) => row.objectKind === KEK_LEVEL_OBJECT_KIND);
131
+ for (const batch of batches(levels))
132
+ await tx
133
+ .delete(bundles)
134
+ .where(and(eq(bundles.objectKind, KEK_LEVEL_OBJECT_KIND), inArray(bundles.objectId, batch.map((row) => row.objectId)), notInArray(bundles.manifestHash, batch.map((row) => row.manifestHash))))
135
+ .run();
136
+ }
106
137
  async function storedDigests(tx, manifestHashes) {
107
138
  const digests = new Map();
108
139
  for (const batch of batches(manifestHashes)) {
@@ -118,94 +149,6 @@ async function storedDigests(tx, manifestHashes) {
118
149
  }
119
150
  return digests;
120
151
  }
121
- /** The requested object first, then each container whose lineage it cites. */
122
- async function containerLineage(db, containerId) {
123
- const lineage = [containerId];
124
- while (lineage.length < MAX_CONTAINER_PATH_LENGTH) {
125
- const [row] = await db
126
- .select({ parentId: containers.parentId })
127
- .from(containers)
128
- .where(eq(containers.id, lineage.at(-1) ?? ""))
129
- .limit(1);
130
- const parentId = row?.parentId;
131
- if (!parentId || lineage.includes(parentId))
132
- break;
133
- lineage.push(parentId);
134
- }
135
- return lineage;
136
- }
137
- async function citedObjects(db, target) {
138
- if (target.kind === "container")
139
- return (await containerLineage(db, target.id)).map((id) => ({
140
- kind: "container",
141
- id,
142
- }));
143
- // Linked containers, containers listing the document before its signed
144
- // placement is checked, and a container whose metadata document it is.
145
- const placements = [
146
- ...(await db
147
- .select({ containerId: documentContainerProjection.containerId })
148
- .from(documentContainerProjection)
149
- .where(eq(documentContainerProjection.documentId, target.id))),
150
- ...(await db
151
- .select({ containerId: pendingDocumentDiscoveries.containerId })
152
- .from(pendingDocumentDiscoveries)
153
- .where(eq(pendingDocumentDiscoveries.documentId, target.id))),
154
- ...(await db
155
- .select({ containerId: containers.id })
156
- .from(containers)
157
- .where(eq(containers.metadataDocumentId, target.id))),
158
- ];
159
- const containerIds = new Set();
160
- for (const { containerId } of placements)
161
- if (containerId)
162
- for (const id of await containerLineage(db, containerId))
163
- containerIds.add(id);
164
- return [
165
- { kind: "document", id: target.id },
166
- ...[...containerIds].map((id) => ({ kind: "container", id })),
167
- ];
168
- }
169
- /**
170
- * The one organization every local source places the read in. Verified
171
- * ownership (stored verified bundles of the target and of the containers on
172
- * its walk, and a document's verified discovery head) and local placement
173
- * (listed container rows reached through links, pending listings, metadata,
174
- * or parents) must all agree: any disagreement, or no source at all, yields
175
- * null, so the read is sent with no hints rather than another
176
- * organization's.
177
- */
178
- async function targetOrganization(db, target, objects) {
179
- const containerIds = objects
180
- .filter((object) => object.kind === "container")
181
- .map((object) => object.id);
182
- const listed = containerIds.length === 0
183
- ? []
184
- : await db
185
- .select({ organizationId: containers.organizationId })
186
- .from(containers)
187
- .where(inArray(containers.id, containerIds));
188
- const verifiedContainers = containerIds.length === 0
189
- ? []
190
- : await db
191
- .select({ organizationId: bundles.organizationId })
192
- .from(bundles)
193
- .where(and(eq(bundles.objectKind, "container"), inArray(bundles.objectId, containerIds)));
194
- const verifiedTarget = await db
195
- .select({ organizationId: bundles.organizationId })
196
- .from(bundles)
197
- .where(and(eq(bundles.objectKind, target.kind), eq(bundles.objectId, target.id)));
198
- const discovered = target.kind === "document"
199
- ? await db
200
- .select({ organizationId: documentDiscoveryHeads.organizationId })
201
- .from(documentDiscoveryHeads)
202
- .where(eq(documentDiscoveryHeads.documentId, target.id))
203
- : [];
204
- const sources = [listed, verifiedContainers, verifiedTarget, discovered];
205
- const organizations = new Set(sources.flat().map((row) => row.organizationId));
206
- const [organizationId] = organizations;
207
- return organizations.size === 1 && organizationId ? organizationId : null;
208
- }
209
152
  const storedColumns = {
210
153
  bundleDigest: bundles.bundleDigest,
211
154
  bundleJson: bundles.bundleJson,
@@ -237,17 +180,36 @@ function recentRows(db, organizationId, limit) {
237
180
  return db
238
181
  .select(storedColumns)
239
182
  .from(bundles)
240
- .where(eq(bundles.organizationId, organizationId))
183
+ .where(and(eq(bundles.organizationId, organizationId), ne(bundles.objectKind, KEK_LEVEL_OBJECT_KIND)))
241
184
  .orderBy(desc(bundles.verifiedAt), asc(bundles.manifestHash))
242
185
  .limit(limit);
243
186
  }
187
+ /** The latest verified KEK level of each container, in lineage order. */
188
+ async function lineageKekLevels(db, organizationId, objects) {
189
+ const containerIds = objects
190
+ .filter((object) => object.kind === "container")
191
+ .map((object) => object.id);
192
+ if (containerIds.length === 0)
193
+ return [];
194
+ const rows = await db
195
+ .select({ ...storedColumns, objectId: bundles.objectId })
196
+ .from(bundles)
197
+ .where(and(eq(bundles.organizationId, organizationId), eq(bundles.objectKind, KEK_LEVEL_OBJECT_KIND), inArray(bundles.objectId, containerIds)))
198
+ .orderBy(desc(bundles.epoch), desc(bundles.verifiedAt));
199
+ const latest = new Map();
200
+ for (const row of rows)
201
+ if (!latest.has(row.objectId))
202
+ latest.set(row.objectId, row);
203
+ return containerIds.flatMap((id) => latest.get(id) ?? []);
204
+ }
244
205
  /**
245
- * Verified bundles of the target's object and of the containers its
246
- * projection walks, newest epochs first; then the most recently verified rows
247
- * of its organization, since a just-verified parent is the likeliest part of
248
- * a new child's projection. Citations never leave the target's organization:
249
- * an object whose organization the device cannot place cites nothing.
250
- * Damaged rows are dropped.
206
+ * The latest verified KEK level of each container the target's projection
207
+ * walks; then verified bundles of those objects, newest epochs first; then
208
+ * the most recently verified bundles of its organization, since a
209
+ * just-verified parent is the likeliest part of a new child's projection.
210
+ * Citations never leave the target's organization: an object whose
211
+ * organization the device cannot place cites nothing. Damaged rows are
212
+ * dropped.
251
213
  */
252
214
  export async function loadVerifiedManifestCitations(execSql, target, limit = KNOWN_MANIFEST_HINT_LIMIT) {
253
215
  await ensureSqlTables(execSql, [
@@ -273,6 +235,15 @@ export async function loadVerifiedManifestCitations(execSql, target, limit = KNO
273
235
  const organizationId = await targetOrganization(db, target, objects);
274
236
  if (organizationId === null)
275
237
  return [];
238
+ for (const row of await lineageKekLevels(db, organizationId, objects)) {
239
+ if (cited.size >= limit)
240
+ break;
241
+ const level = await readStoredKekLevel(row);
242
+ if (level)
243
+ cited.set(row.manifestHash, level);
244
+ else
245
+ damaged.push(row.manifestHash);
246
+ }
276
247
  for (const object of objects)
277
248
  if (cited.size < limit)
278
249
  await collect(await objectRows(db, organizationId, object, limit - cited.size));
@@ -1 +1 @@
1
- {"version":3,"file":"verifiedManifestBundlePersistence.js","sourceRoot":"","sources":["verifiedManifestBundlePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n VerifiedManifestCitations,\n WriterProjectionTarget,\n} from \"@tearleads/api-client\";\nimport { toFingerprint } from \"@tearleads/crypto\";\nimport {\n type AccessManifestBundleWireResponse,\n isAccessManifestBundleWireResponse,\n} from \"@tearleads/validators/response\";\nimport {\n KNOWN_MANIFEST_HINT_LIMIT,\n MAX_CONTAINER_PATH_LENGTH,\n} from \"@tearleads/validators/util\";\nimport { and, asc, desc, eq, inArray, sql } from \"drizzle-orm\";\nimport {\n containerTables,\n documentContainerProjection,\n documentContainerProjectionTables,\n} from \"../sqlite/schema\";\nimport { containers } from \"../sqlite/schema/containerSchema\";\nimport {\n documentDiscoveryHeads,\n pendingDocumentDiscoveries,\n} from \"../sqlite/schema/documentDiscoveryEvidenceSchema\";\nimport {\n verifiedAccessManifestBundles as bundles,\n verifiedManifestBundleTables,\n} from \"../sqlite/schema/verifiedManifestBundleSchema\";\nimport { sqliteInsertBatchSize } from \"../sqlite/sqliteBindBudget\";\nimport {\n type ClientSQLiteDatabase,\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../sqlite/sqlitePersistenceRuntime\";\nimport { excludedValue } from \"../sqlite/sqliteUpsert\";\nimport { type ExecSql, ensureSqlTables } from \"../sqlite/sqlSchema\";\n\n/**\n * Bounds local bytes (a bundle is ~10–20 KB). Evicting the least recently\n * verified rows only makes a later read carry those bundles again.\n */\nexport const MAX_VERIFIED_MANIFEST_BUNDLES = 1024;\nconst STATEMENT_BATCH = 200;\n\nexport interface VerifiedManifestBundle {\n readonly bundle: AccessManifestBundleWireResponse;\n readonly checkpoint: {\n readonly objectKind: string;\n readonly organizationId: string;\n readonly objectId: string;\n readonly epoch: number;\n };\n}\n\nfunction sha256Hex(text: string): Promise<string> {\n return toFingerprint(new TextEncoder().encode(text));\n}\n\nconst BUNDLE_WRITE_BATCH_SIZE = sqliteInsertBatchSize(bundles);\n\nfunction slices<T>(values: readonly T[], size: number): T[][] {\n const result: T[][] = [];\n for (let index = 0; index < values.length; index += size)\n result.push(values.slice(index, index + size));\n return result;\n}\n\nfunction batches<T>(values: readonly T[]): T[][] {\n return slices(values, STATEMENT_BATCH);\n}\n\nasync function pruneInTransaction(\n tx: ClientSQLiteTransactionScope,\n): Promise<void> {\n const [row] = await tx.select({ count: sql<number>`count(*)` }).from(bundles);\n const excess = (row?.count ?? 0) - MAX_VERIFIED_MANIFEST_BUNDLES;\n if (excess <= 0) return;\n const evicted = await tx\n .select({ manifestHash: bundles.manifestHash })\n .from(bundles)\n .orderBy(asc(bundles.verifiedAt), asc(bundles.manifestHash))\n .limit(excess);\n for (const batch of batches(evicted.map((entry) => entry.manifestHash)))\n await tx.delete(bundles).where(inArray(bundles.manifestHash, batch)).run();\n}\n\n/** A verified bundle's exact JSON and that JSON's digest. */\nexport type VerifiedManifestBundleRow = Omit<\n typeof bundles.$inferInsert,\n \"verifiedAt\"\n>;\n\n/**\n * Prepares rows for bundles that just passed projection verification. The\n * digests are computed here, before the checkpoint transaction that writes\n * them begins.\n */\nexport function verifiedManifestBundleRows(\n entries: readonly VerifiedManifestBundle[],\n): Promise<VerifiedManifestBundleRow[]> {\n const unique = new Map(\n entries.map((entry) => [entry.bundle.manifestHash, entry]),\n );\n return Promise.all(\n [...unique.values()].map(async ({ bundle, checkpoint }) => {\n const bundleJson = JSON.stringify(bundle);\n return {\n manifestHash: bundle.manifestHash,\n organizationId: checkpoint.organizationId,\n objectKind: checkpoint.objectKind,\n objectId: checkpoint.objectId,\n epoch: checkpoint.epoch,\n bundleJson,\n bundleDigest: await sha256Hex(bundleJson),\n };\n }),\n );\n}\n\n/**\n * Records verified bundles inside the transaction that finalizes their\n * projection's checkpoints, so they commit or roll back with it and no cache\n * work outlives the verification. Freshly verified bytes replace any stored\n * copy.\n */\nexport async function writeVerifiedManifestBundlesInTransaction(\n tx: ClientSQLiteTransactionScope,\n rows: readonly VerifiedManifestBundleRow[],\n verifiedAt: string,\n): Promise<void> {\n if (rows.length === 0) return;\n const stored = await storedDigests(\n tx,\n rows.map((row) => row.manifestHash),\n );\n // An identical stored row only needs its recency refreshed.\n const unchanged = rows.filter(\n (row) => stored.get(row.manifestHash) === row.bundleDigest,\n );\n for (const batch of batches(unchanged.map((row) => row.manifestHash)))\n await tx\n .update(bundles)\n .set({ verifiedAt })\n .where(inArray(bundles.manifestHash, batch))\n .run();\n // A repeated hash keeps its last changed row, as sequential upserts would.\n const changed = new Map(\n rows\n .filter((row) => stored.get(row.manifestHash) !== row.bundleDigest)\n .map((row) => [row.manifestHash, { ...row, verifiedAt }]),\n );\n for (const batch of slices([...changed.values()], BUNDLE_WRITE_BATCH_SIZE))\n await tx\n .insert(bundles)\n .values(batch)\n .onConflictDoUpdate({\n target: bundles.manifestHash,\n set: {\n organizationId: excludedValue(bundles.organizationId),\n objectKind: excludedValue(bundles.objectKind),\n objectId: excludedValue(bundles.objectId),\n epoch: excludedValue(bundles.epoch),\n bundleJson: excludedValue(bundles.bundleJson),\n bundleDigest: excludedValue(bundles.bundleDigest),\n verifiedAt: excludedValue(bundles.verifiedAt),\n },\n })\n .run();\n await pruneInTransaction(tx);\n}\n\nasync function storedDigests(\n tx: ClientSQLiteTransactionScope,\n manifestHashes: readonly string[],\n): Promise<Map<string, string>> {\n const digests = new Map<string, string>();\n for (const batch of batches(manifestHashes)) {\n const rows = await tx\n .select({\n bundleDigest: bundles.bundleDigest,\n manifestHash: bundles.manifestHash,\n })\n .from(bundles)\n .where(inArray(bundles.manifestHash, batch));\n for (const row of rows) digests.set(row.manifestHash, row.bundleDigest);\n }\n return digests;\n}\n\n/** The requested object first, then each container whose lineage it cites. */\nasync function containerLineage(\n db: ClientSQLiteDatabase,\n containerId: string,\n): Promise<string[]> {\n const lineage = [containerId];\n while (lineage.length < MAX_CONTAINER_PATH_LENGTH) {\n const [row] = await db\n .select({ parentId: containers.parentId })\n .from(containers)\n .where(eq(containers.id, lineage.at(-1) ?? \"\"))\n .limit(1);\n const parentId = row?.parentId;\n if (!parentId || lineage.includes(parentId)) break;\n lineage.push(parentId);\n }\n return lineage;\n}\n\nasync function citedObjects(\n db: ClientSQLiteDatabase,\n target: WriterProjectionTarget,\n): Promise<{ readonly kind: string; readonly id: string }[]> {\n if (target.kind === \"container\")\n return (await containerLineage(db, target.id)).map((id) => ({\n kind: \"container\",\n id,\n }));\n // Linked containers, containers listing the document before its signed\n // placement is checked, and a container whose metadata document it is.\n const placements = [\n ...(await db\n .select({ containerId: documentContainerProjection.containerId })\n .from(documentContainerProjection)\n .where(eq(documentContainerProjection.documentId, target.id))),\n ...(await db\n .select({ containerId: pendingDocumentDiscoveries.containerId })\n .from(pendingDocumentDiscoveries)\n .where(eq(pendingDocumentDiscoveries.documentId, target.id))),\n ...(await db\n .select({ containerId: containers.id })\n .from(containers)\n .where(eq(containers.metadataDocumentId, target.id))),\n ];\n const containerIds = new Set<string>();\n for (const { containerId } of placements)\n if (containerId)\n for (const id of await containerLineage(db, containerId))\n containerIds.add(id);\n return [\n { kind: \"document\", id: target.id },\n ...[...containerIds].map((id) => ({ kind: \"container\", id })),\n ];\n}\n\n/**\n * The one organization every local source places the read in. Verified\n * ownership (stored verified bundles of the target and of the containers on\n * its walk, and a document's verified discovery head) and local placement\n * (listed container rows reached through links, pending listings, metadata,\n * or parents) must all agree: any disagreement, or no source at all, yields\n * null, so the read is sent with no hints rather than another\n * organization's.\n */\nasync function targetOrganization(\n db: ClientSQLiteDatabase,\n target: WriterProjectionTarget,\n objects: readonly { readonly kind: string; readonly id: string }[],\n): Promise<string | null> {\n const containerIds = objects\n .filter((object) => object.kind === \"container\")\n .map((object) => object.id);\n const listed =\n containerIds.length === 0\n ? []\n : await db\n .select({ organizationId: containers.organizationId })\n .from(containers)\n .where(inArray(containers.id, containerIds));\n const verifiedContainers =\n containerIds.length === 0\n ? []\n : await db\n .select({ organizationId: bundles.organizationId })\n .from(bundles)\n .where(\n and(\n eq(bundles.objectKind, \"container\"),\n inArray(bundles.objectId, containerIds),\n ),\n );\n const verifiedTarget = await db\n .select({ organizationId: bundles.organizationId })\n .from(bundles)\n .where(\n and(eq(bundles.objectKind, target.kind), eq(bundles.objectId, target.id)),\n );\n const discovered =\n target.kind === \"document\"\n ? await db\n .select({ organizationId: documentDiscoveryHeads.organizationId })\n .from(documentDiscoveryHeads)\n .where(eq(documentDiscoveryHeads.documentId, target.id))\n : [];\n const sources = [listed, verifiedContainers, verifiedTarget, discovered];\n const organizations = new Set(\n sources.flat().map((row) => row.organizationId),\n );\n const [organizationId] = organizations;\n return organizations.size === 1 && organizationId ? organizationId : null;\n}\n\ninterface StoredBundleRow {\n readonly bundleDigest: string;\n readonly bundleJson: string;\n readonly manifestHash: string;\n}\n\nconst storedColumns = {\n bundleDigest: bundles.bundleDigest,\n bundleJson: bundles.bundleJson,\n manifestHash: bundles.manifestHash,\n};\n\nasync function readStoredBundle(\n row: StoredBundleRow,\n): Promise<AccessManifestBundleWireResponse | null> {\n if ((await sha256Hex(row.bundleJson)) !== row.bundleDigest) return null;\n try {\n const bundle: unknown = JSON.parse(row.bundleJson);\n return isAccessManifestBundleWireResponse(bundle) &&\n bundle.manifestHash === row.manifestHash\n ? bundle\n : null;\n } catch {\n return null;\n }\n}\n\nfunction objectRows(\n db: ClientSQLiteDatabase,\n organizationId: string,\n object: { readonly kind: string; readonly id: string },\n limit: number,\n) {\n return db\n .select(storedColumns)\n .from(bundles)\n .where(\n and(\n eq(bundles.organizationId, organizationId),\n eq(bundles.objectKind, object.kind),\n eq(bundles.objectId, object.id),\n ),\n )\n .orderBy(desc(bundles.epoch))\n .limit(limit);\n}\n\nfunction recentRows(\n db: ClientSQLiteDatabase,\n organizationId: string,\n limit: number,\n) {\n return db\n .select(storedColumns)\n .from(bundles)\n .where(eq(bundles.organizationId, organizationId))\n .orderBy(desc(bundles.verifiedAt), asc(bundles.manifestHash))\n .limit(limit);\n}\n\n/**\n * Verified bundles of the target's object and of the containers its\n * projection walks, newest epochs first; then the most recently verified rows\n * of its organization, since a just-verified parent is the likeliest part of\n * a new child's projection. Citations never leave the target's organization:\n * an object whose organization the device cannot place cites nothing.\n * Damaged rows are dropped.\n */\nexport async function loadVerifiedManifestCitations(\n execSql: ExecSql,\n target: WriterProjectionTarget,\n limit = KNOWN_MANIFEST_HINT_LIMIT,\n): Promise<AccessManifestBundleWireResponse[]> {\n await ensureSqlTables(execSql, [\n ...verifiedManifestBundleTables,\n ...containerTables,\n ...documentContainerProjectionTables,\n ]);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const cited = new Map<string, AccessManifestBundleWireResponse>();\n const damaged: string[] = [];\n const collect = async (rows: readonly StoredBundleRow[]) => {\n for (const row of rows) {\n if (cited.size >= limit || cited.has(row.manifestHash)) continue;\n const bundle = await readStoredBundle(row);\n if (bundle) cited.set(row.manifestHash, bundle);\n else damaged.push(row.manifestHash);\n }\n };\n const objects = await citedObjects(db, target);\n const organizationId = await targetOrganization(db, target, objects);\n if (organizationId === null) return [];\n for (const object of objects)\n if (cited.size < limit)\n await collect(\n await objectRows(db, organizationId, object, limit - cited.size),\n );\n if (cited.size < limit)\n await collect(await recentRows(db, organizationId, limit + cited.size));\n if (damaged.length > 0)\n await getClientSQLitePersistenceRuntime(execSql)\n .runMutation((handle) =>\n handle\n .delete(bundles)\n .where(inArray(bundles.manifestHash, damaged))\n .run(),\n )\n .catch(() => undefined);\n return [...cited.values()];\n}\n\n/** Cites from whichever database the signed-in identity currently uses. */\nexport function createVerifiedManifestCitations(\n resolveExecSql: () => ExecSql | null,\n): VerifiedManifestCitations {\n return {\n cite: async (target) => {\n const execSql = resolveExecSql();\n return execSql ? loadVerifiedManifestCitations(execSql, target) : [];\n },\n };\n}\n\n/** Drops an organization's verified bundles when a remote reset forgets it. */\nexport async function deleteVerifiedManifestBundlesForOrganization(\n tx: ClientSQLiteTransactionScope,\n organizationId: string,\n): Promise<void> {\n await tx\n .delete(bundles)\n .where(eq(bundles.organizationId, organizationId))\n .run();\n}\n"]}
1
+ {"version":3,"file":"verifiedManifestBundlePersistence.js","sourceRoot":"","sources":["verifiedManifestBundlePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n HeldProjectionCitation,\n VerifiedManifestCitations,\n WriterProjectionTarget,\n} from \"@tearleads/api-client\";\nimport { toFingerprint } from \"@tearleads/crypto\";\nimport {\n type AccessManifestBundleWireResponse,\n isAccessManifestBundleWireResponse,\n} from \"@tearleads/validators/response\";\nimport { KNOWN_MANIFEST_HINT_LIMIT } from \"@tearleads/validators/util\";\nimport { and, asc, desc, eq, inArray, ne, notInArray, sql } from \"drizzle-orm\";\nimport {\n containerTables,\n documentContainerProjectionTables,\n} from \"../sqlite/schema\";\nimport {\n verifiedAccessManifestBundles as bundles,\n verifiedManifestBundleTables,\n} from \"../sqlite/schema/verifiedManifestBundleSchema\";\nimport { sqliteInsertBatchSize } from \"../sqlite/sqliteBindBudget\";\nimport {\n type ClientSQLiteDatabase,\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../sqlite/sqlitePersistenceRuntime\";\nimport { excludedValue } from \"../sqlite/sqliteUpsert\";\nimport { type ExecSql, ensureSqlTables } from \"../sqlite/sqlSchema\";\nimport {\n type CitedObject,\n citedObjects,\n targetOrganization,\n} from \"./verifiedCitations/citationScope\";\nimport {\n KEK_LEVEL_OBJECT_KIND,\n kekLevelRow,\n readStoredKekLevel,\n type VerifiedKekLevel,\n} from \"./verifiedCitations/kekLevelCitations\";\n\n/**\n * Bounds local bytes (a bundle is ~10–20 KB). Evicting the least recently\n * verified rows only makes a later read carry those bundles again.\n */\nexport const MAX_VERIFIED_MANIFEST_BUNDLES = 1024;\nconst STATEMENT_BATCH = 200;\n\ninterface VerifiedBundle {\n readonly bundle: AccessManifestBundleWireResponse;\n readonly checkpoint: {\n readonly objectKind: string;\n readonly organizationId: string;\n readonly objectId: string;\n readonly epoch: number;\n };\n}\n\n/** Bytes a projection verification accepted: a bundle or a KEK level. */\nexport type VerifiedManifestBundle = VerifiedBundle | VerifiedKekLevel;\n\nfunction sha256Hex(text: string): Promise<string> {\n return toFingerprint(new TextEncoder().encode(text));\n}\n\nconst BUNDLE_WRITE_BATCH_SIZE = sqliteInsertBatchSize(bundles);\n\nfunction slices<T>(values: readonly T[], size: number): T[][] {\n const result: T[][] = [];\n for (let index = 0; index < values.length; index += size)\n result.push(values.slice(index, index + size));\n return result;\n}\n\nfunction batches<T>(values: readonly T[]): T[][] {\n return slices(values, STATEMENT_BATCH);\n}\n\nasync function pruneInTransaction(\n tx: ClientSQLiteTransactionScope,\n): Promise<void> {\n const [row] = await tx.select({ count: sql<number>`count(*)` }).from(bundles);\n const excess = (row?.count ?? 0) - MAX_VERIFIED_MANIFEST_BUNDLES;\n if (excess <= 0) return;\n const evicted = await tx\n .select({ manifestHash: bundles.manifestHash })\n .from(bundles)\n .orderBy(asc(bundles.verifiedAt), asc(bundles.manifestHash))\n .limit(excess);\n for (const batch of batches(evicted.map((entry) => entry.manifestHash)))\n await tx.delete(bundles).where(inArray(bundles.manifestHash, batch)).run();\n}\n\n/** A verified bundle's exact JSON and that JSON's digest. */\nexport type VerifiedManifestBundleRow = Omit<\n typeof bundles.$inferInsert,\n \"verifiedAt\"\n>;\n\n/**\n * Prepares rows for bundles and KEK levels that just passed projection\n * verification. The digests are computed here, before the checkpoint\n * transaction that writes them begins.\n */\nexport async function verifiedManifestBundleRows(\n entries: readonly VerifiedManifestBundle[],\n): Promise<VerifiedManifestBundleRow[]> {\n const bundleEntries: VerifiedBundle[] = [];\n const levelEntries: VerifiedKekLevel[] = [];\n for (const entry of entries)\n if (\"kekLevel\" in entry) levelEntries.push(entry);\n else bundleEntries.push(entry);\n // One level per container: its newest epoch, the last one seen on a tie.\n const levelRows = new Map<string, VerifiedManifestBundleRow>();\n for (const level of levelEntries) {\n const row = await kekLevelRow(level);\n const held = row && levelRows.get(row.objectId);\n if (row && (!held || held.epoch <= row.epoch))\n levelRows.set(row.objectId, row);\n }\n return [...(await bundleRows(bundleEntries)), ...levelRows.values()];\n}\n\nfunction bundleRows(\n entries: readonly VerifiedBundle[],\n): Promise<VerifiedManifestBundleRow[]> {\n const unique = new Map(\n entries.map((entry) => [entry.bundle.manifestHash, entry]),\n );\n return Promise.all(\n [...unique.values()].map(async ({ bundle, checkpoint }) => {\n const bundleJson = JSON.stringify(bundle);\n return {\n manifestHash: bundle.manifestHash,\n organizationId: checkpoint.organizationId,\n objectKind: checkpoint.objectKind,\n objectId: checkpoint.objectId,\n epoch: checkpoint.epoch,\n bundleJson,\n bundleDigest: await sha256Hex(bundleJson),\n };\n }),\n );\n}\n\n/**\n * Records verified bundles inside the transaction that finalizes their\n * projection's checkpoints, so they commit or roll back with it and no cache\n * work outlives the verification. Freshly verified bytes replace any stored\n * copy.\n */\nexport async function writeVerifiedManifestBundlesInTransaction(\n tx: ClientSQLiteTransactionScope,\n rows: readonly VerifiedManifestBundleRow[],\n verifiedAt: string,\n): Promise<void> {\n if (rows.length === 0) return;\n const stored = await storedDigests(\n tx,\n rows.map((row) => row.manifestHash),\n );\n // An identical stored row only needs its recency refreshed.\n const unchanged = rows.filter(\n (row) => stored.get(row.manifestHash) === row.bundleDigest,\n );\n for (const batch of batches(unchanged.map((row) => row.manifestHash)))\n await tx\n .update(bundles)\n .set({ verifiedAt })\n .where(inArray(bundles.manifestHash, batch))\n .run();\n // A repeated hash keeps its last changed row, as sequential upserts would.\n const changed = new Map(\n rows\n .filter((row) => stored.get(row.manifestHash) !== row.bundleDigest)\n .map((row) => [row.manifestHash, { ...row, verifiedAt }]),\n );\n for (const batch of slices([...changed.values()], BUNDLE_WRITE_BATCH_SIZE))\n await tx\n .insert(bundles)\n .values(batch)\n .onConflictDoUpdate({\n target: bundles.manifestHash,\n set: {\n organizationId: excludedValue(bundles.organizationId),\n objectKind: excludedValue(bundles.objectKind),\n objectId: excludedValue(bundles.objectId),\n epoch: excludedValue(bundles.epoch),\n bundleJson: excludedValue(bundles.bundleJson),\n bundleDigest: excludedValue(bundles.bundleDigest),\n verifiedAt: excludedValue(bundles.verifiedAt),\n },\n })\n .run();\n await dropSupersededKekLevels(tx, rows);\n await pruneInTransaction(tx);\n}\n\n/**\n * A container keeps only its latest verified level: the API serves its\n * current level, so an older one can never be cited usefully again.\n */\nasync function dropSupersededKekLevels(\n tx: ClientSQLiteTransactionScope,\n rows: readonly VerifiedManifestBundleRow[],\n): Promise<void> {\n const levels = rows.filter((row) => row.objectKind === KEK_LEVEL_OBJECT_KIND);\n for (const batch of batches(levels))\n await tx\n .delete(bundles)\n .where(\n and(\n eq(bundles.objectKind, KEK_LEVEL_OBJECT_KIND),\n inArray(\n bundles.objectId,\n batch.map((row) => row.objectId),\n ),\n notInArray(\n bundles.manifestHash,\n batch.map((row) => row.manifestHash),\n ),\n ),\n )\n .run();\n}\n\nasync function storedDigests(\n tx: ClientSQLiteTransactionScope,\n manifestHashes: readonly string[],\n): Promise<Map<string, string>> {\n const digests = new Map<string, string>();\n for (const batch of batches(manifestHashes)) {\n const rows = await tx\n .select({\n bundleDigest: bundles.bundleDigest,\n manifestHash: bundles.manifestHash,\n })\n .from(bundles)\n .where(inArray(bundles.manifestHash, batch));\n for (const row of rows) digests.set(row.manifestHash, row.bundleDigest);\n }\n return digests;\n}\n\ninterface StoredBundleRow {\n readonly bundleDigest: string;\n readonly bundleJson: string;\n readonly manifestHash: string;\n}\n\nconst storedColumns = {\n bundleDigest: bundles.bundleDigest,\n bundleJson: bundles.bundleJson,\n manifestHash: bundles.manifestHash,\n};\n\nasync function readStoredBundle(\n row: StoredBundleRow,\n): Promise<AccessManifestBundleWireResponse | null> {\n if ((await sha256Hex(row.bundleJson)) !== row.bundleDigest) return null;\n try {\n const bundle: unknown = JSON.parse(row.bundleJson);\n return isAccessManifestBundleWireResponse(bundle) &&\n bundle.manifestHash === row.manifestHash\n ? bundle\n : null;\n } catch {\n return null;\n }\n}\n\nfunction objectRows(\n db: ClientSQLiteDatabase,\n organizationId: string,\n object: CitedObject,\n limit: number,\n) {\n return db\n .select(storedColumns)\n .from(bundles)\n .where(\n and(\n eq(bundles.organizationId, organizationId),\n eq(bundles.objectKind, object.kind),\n eq(bundles.objectId, object.id),\n ),\n )\n .orderBy(desc(bundles.epoch))\n .limit(limit);\n}\n\nfunction recentRows(\n db: ClientSQLiteDatabase,\n organizationId: string,\n limit: number,\n) {\n return db\n .select(storedColumns)\n .from(bundles)\n .where(\n and(\n eq(bundles.organizationId, organizationId),\n ne(bundles.objectKind, KEK_LEVEL_OBJECT_KIND),\n ),\n )\n .orderBy(desc(bundles.verifiedAt), asc(bundles.manifestHash))\n .limit(limit);\n}\n\n/** The latest verified KEK level of each container, in lineage order. */\nasync function lineageKekLevels(\n db: ClientSQLiteDatabase,\n organizationId: string,\n objects: readonly CitedObject[],\n): Promise<StoredBundleRow[]> {\n const containerIds = objects\n .filter((object) => object.kind === \"container\")\n .map((object) => object.id);\n if (containerIds.length === 0) return [];\n const rows = await db\n .select({ ...storedColumns, objectId: bundles.objectId })\n .from(bundles)\n .where(\n and(\n eq(bundles.organizationId, organizationId),\n eq(bundles.objectKind, KEK_LEVEL_OBJECT_KIND),\n inArray(bundles.objectId, containerIds),\n ),\n )\n .orderBy(desc(bundles.epoch), desc(bundles.verifiedAt));\n const latest = new Map<string, StoredBundleRow>();\n for (const row of rows)\n if (!latest.has(row.objectId)) latest.set(row.objectId, row);\n return containerIds.flatMap((id) => latest.get(id) ?? []);\n}\n\n/**\n * The latest verified KEK level of each container the target's projection\n * walks; then verified bundles of those objects, newest epochs first; then\n * the most recently verified bundles of its organization, since a\n * just-verified parent is the likeliest part of a new child's projection.\n * Citations never leave the target's organization: an object whose\n * organization the device cannot place cites nothing. Damaged rows are\n * dropped.\n */\nexport async function loadVerifiedManifestCitations(\n execSql: ExecSql,\n target: WriterProjectionTarget,\n limit = KNOWN_MANIFEST_HINT_LIMIT,\n): Promise<HeldProjectionCitation[]> {\n await ensureSqlTables(execSql, [\n ...verifiedManifestBundleTables,\n ...containerTables,\n ...documentContainerProjectionTables,\n ]);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const cited = new Map<string, HeldProjectionCitation>();\n const damaged: string[] = [];\n const collect = async (rows: readonly StoredBundleRow[]) => {\n for (const row of rows) {\n if (cited.size >= limit || cited.has(row.manifestHash)) continue;\n const bundle = await readStoredBundle(row);\n if (bundle) cited.set(row.manifestHash, bundle);\n else damaged.push(row.manifestHash);\n }\n };\n const objects = await citedObjects(db, target);\n const organizationId = await targetOrganization(db, target, objects);\n if (organizationId === null) return [];\n for (const row of await lineageKekLevels(db, organizationId, objects)) {\n if (cited.size >= limit) break;\n const level = await readStoredKekLevel(row);\n if (level) cited.set(row.manifestHash, level);\n else damaged.push(row.manifestHash);\n }\n for (const object of objects)\n if (cited.size < limit)\n await collect(\n await objectRows(db, organizationId, object, limit - cited.size),\n );\n if (cited.size < limit)\n await collect(await recentRows(db, organizationId, limit + cited.size));\n if (damaged.length > 0)\n await getClientSQLitePersistenceRuntime(execSql)\n .runMutation((handle) =>\n handle\n .delete(bundles)\n .where(inArray(bundles.manifestHash, damaged))\n .run(),\n )\n .catch(() => undefined);\n return [...cited.values()];\n}\n\n/** Cites from whichever database the signed-in identity currently uses. */\nexport function createVerifiedManifestCitations(\n resolveExecSql: () => ExecSql | null,\n): VerifiedManifestCitations {\n return {\n cite: async (target) => {\n const execSql = resolveExecSql();\n return execSql ? loadVerifiedManifestCitations(execSql, target) : [];\n },\n };\n}\n\n/** Drops an organization's verified bundles when a remote reset forgets it. */\nexport async function deleteVerifiedManifestBundlesForOrganization(\n tx: ClientSQLiteTransactionScope,\n organizationId: string,\n): Promise<void> {\n await tx\n .delete(bundles)\n .where(eq(bundles.organizationId, organizationId))\n .run();\n}\n"]}
@@ -6,10 +6,15 @@ import { type SqlTableSchema } from "../sqlTableSchema.js";
6
6
  * verifies the rebuilt projection from scratch. Rows are neither trust nor
7
7
  * checkpoint state: a missing, evicted, or damaged row only costs bytes.
8
8
  *
9
+ * Container KEK levels share the table as typed rows (`objectKind`
10
+ * `container-kek-level`), keyed by the SHA-256 of their canonical wire bytes.
11
+ *
9
12
  * Columns:
10
- * - `manifestHash`: Manifest hash; equals the stored bundle's `manifestHash`.
13
+ * - `manifestHash`: Citation key: the stored bundle's `manifestHash`, or a
14
+ * level row's digest.
11
15
  * - `organizationId`, `objectKind`, `objectId`, `epoch`: The verified
12
- * manifest's object, used to cite the lineage relevant to a request.
16
+ * manifest's object (or the level's container and key epoch), used to cite
17
+ * the lineage relevant to a request.
13
18
  * - `bundleJson`: The exact verified bundle as JSON.
14
19
  * - `bundleDigest`: SHA-256 hex of `bundleJson`; a mismatch drops the row.
15
20
  * - `verifiedAt`: Latest local verification time; oldest rows evict first.
@@ -8,10 +8,15 @@ import { defineSqlTableSchema } from "../sqlTableSchema.js";
8
8
  * verifies the rebuilt projection from scratch. Rows are neither trust nor
9
9
  * checkpoint state: a missing, evicted, or damaged row only costs bytes.
10
10
  *
11
+ * Container KEK levels share the table as typed rows (`objectKind`
12
+ * `container-kek-level`), keyed by the SHA-256 of their canonical wire bytes.
13
+ *
11
14
  * Columns:
12
- * - `manifestHash`: Manifest hash; equals the stored bundle's `manifestHash`.
15
+ * - `manifestHash`: Citation key: the stored bundle's `manifestHash`, or a
16
+ * level row's digest.
13
17
  * - `organizationId`, `objectKind`, `objectId`, `epoch`: The verified
14
- * manifest's object, used to cite the lineage relevant to a request.
18
+ * manifest's object (or the level's container and key epoch), used to cite
19
+ * the lineage relevant to a request.
15
20
  * - `bundleJson`: The exact verified bundle as JSON.
16
21
  * - `bundleDigest`: SHA-256 hex of `bundleJson`; a mismatch drops the row.
17
22
  * - `verifiedAt`: Latest local verification time; oldest rows evict first.
@@ -1 +1 @@
1
- {"version":3,"file":"verifiedManifestBundleSchema.js","sourceRoot":"","sources":["verifiedManifestBundleSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAuB,MAAM,mBAAmB,CAAC;AAE9E;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,WAAW,CACtD,kCAAkC,EAClC;IACE,YAAY,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,UAAU,EAAE;IAChD,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,EAAE;IACjD,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE;IACzC,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE;IACrC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE;IACjC,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE;IACzC,YAAY,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE;IAC7C,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE;CAC1C,EACD,CAAC,KAAK,EAAE,EAAE,CAAC;IACT,KAAK,CAAC,6CAA6C,CAAC,CAAC,EAAE,CACrD,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,QAAQ,EACd,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAClB;IACD,mEAAmE;IACnE,KAAK,CAAC,+CAA+C,CAAC,CAAC,EAAE,CACvD,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,YAAY,CACnB;IACD,kEAAkE;IAClE,KAAK,CAAC,0DAA0D,CAAC,CAAC,EAAE,CAClE,KAAK,CAAC,cAAc,EACpB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EACtB,KAAK,CAAC,YAAY,CACnB;CACF,CACF,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAkC;IACzE,oBAAoB,CAAC,6BAA6B,CAAC;CACpD,CAAC","sourcesContent":["import { desc } from \"drizzle-orm\";\nimport { index, integer, sqliteTable, text } from \"drizzle-orm/sqlite-core\";\nimport { defineSqlTableSchema, type SqlTableSchema } from \"../sqlTableSchema\";\n\n/**\n * Access-manifest bundles this device verified inside a writer projection,\n * keyed by manifest hash. A later projection read cites them so the API can\n * omit their bytes; the client refills each omission from this row and\n * verifies the rebuilt projection from scratch. Rows are neither trust nor\n * checkpoint state: a missing, evicted, or damaged row only costs bytes.\n *\n * Columns:\n * - `manifestHash`: Manifest hash; equals the stored bundle's `manifestHash`.\n * - `organizationId`, `objectKind`, `objectId`, `epoch`: The verified\n * manifest's object, used to cite the lineage relevant to a request.\n * - `bundleJson`: The exact verified bundle as JSON.\n * - `bundleDigest`: SHA-256 hex of `bundleJson`; a mismatch drops the row.\n * - `verifiedAt`: Latest local verification time; oldest rows evict first.\n */\nexport const verifiedAccessManifestBundles = sqliteTable(\n \"verified_access_manifest_bundles\",\n {\n manifestHash: text(\"manifest_hash\").primaryKey(),\n organizationId: text(\"organization_id\").notNull(),\n objectKind: text(\"object_kind\").notNull(),\n objectId: text(\"object_id\").notNull(),\n epoch: integer(\"epoch\").notNull(),\n bundleJson: text(\"bundle_json\").notNull(),\n bundleDigest: text(\"bundle_digest\").notNull(),\n verifiedAt: text(\"verified_at\").notNull(),\n },\n (table) => [\n index(\"verified_access_manifest_bundles_object_idx\").on(\n table.objectKind,\n table.objectId,\n desc(table.epoch),\n ),\n // Eviction order: oldest verification first, hash as the tiebreak.\n index(\"verified_access_manifest_bundles_eviction_idx\").on(\n table.verifiedAt,\n table.manifestHash,\n ),\n // Recent citations: an organization's newest verifications first.\n index(\"verified_access_manifest_bundles_organization_recent_idx\").on(\n table.organizationId,\n desc(table.verifiedAt),\n table.manifestHash,\n ),\n ],\n);\n\nexport const verifiedManifestBundleTables: ReadonlyArray<SqlTableSchema> = [\n defineSqlTableSchema(verifiedAccessManifestBundles),\n];\n"]}
1
+ {"version":3,"file":"verifiedManifestBundleSchema.js","sourceRoot":"","sources":["verifiedManifestBundleSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAuB,MAAM,mBAAmB,CAAC;AAE9E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,WAAW,CACtD,kCAAkC,EAClC;IACE,YAAY,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,UAAU,EAAE;IAChD,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,EAAE;IACjD,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE;IACzC,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE;IACrC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE;IACjC,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE;IACzC,YAAY,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE;IAC7C,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE;CAC1C,EACD,CAAC,KAAK,EAAE,EAAE,CAAC;IACT,KAAK,CAAC,6CAA6C,CAAC,CAAC,EAAE,CACrD,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,QAAQ,EACd,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAClB;IACD,mEAAmE;IACnE,KAAK,CAAC,+CAA+C,CAAC,CAAC,EAAE,CACvD,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,YAAY,CACnB;IACD,kEAAkE;IAClE,KAAK,CAAC,0DAA0D,CAAC,CAAC,EAAE,CAClE,KAAK,CAAC,cAAc,EACpB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EACtB,KAAK,CAAC,YAAY,CACnB;CACF,CACF,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAkC;IACzE,oBAAoB,CAAC,6BAA6B,CAAC;CACpD,CAAC","sourcesContent":["import { desc } from \"drizzle-orm\";\nimport { index, integer, sqliteTable, text } from \"drizzle-orm/sqlite-core\";\nimport { defineSqlTableSchema, type SqlTableSchema } from \"../sqlTableSchema\";\n\n/**\n * Access-manifest bundles this device verified inside a writer projection,\n * keyed by manifest hash. A later projection read cites them so the API can\n * omit their bytes; the client refills each omission from this row and\n * verifies the rebuilt projection from scratch. Rows are neither trust nor\n * checkpoint state: a missing, evicted, or damaged row only costs bytes.\n *\n * Container KEK levels share the table as typed rows (`objectKind`\n * `container-kek-level`), keyed by the SHA-256 of their canonical wire bytes.\n *\n * Columns:\n * - `manifestHash`: Citation key: the stored bundle's `manifestHash`, or a\n * level row's digest.\n * - `organizationId`, `objectKind`, `objectId`, `epoch`: The verified\n * manifest's object (or the level's container and key epoch), used to cite\n * the lineage relevant to a request.\n * - `bundleJson`: The exact verified bundle as JSON.\n * - `bundleDigest`: SHA-256 hex of `bundleJson`; a mismatch drops the row.\n * - `verifiedAt`: Latest local verification time; oldest rows evict first.\n */\nexport const verifiedAccessManifestBundles = sqliteTable(\n \"verified_access_manifest_bundles\",\n {\n manifestHash: text(\"manifest_hash\").primaryKey(),\n organizationId: text(\"organization_id\").notNull(),\n objectKind: text(\"object_kind\").notNull(),\n objectId: text(\"object_id\").notNull(),\n epoch: integer(\"epoch\").notNull(),\n bundleJson: text(\"bundle_json\").notNull(),\n bundleDigest: text(\"bundle_digest\").notNull(),\n verifiedAt: text(\"verified_at\").notNull(),\n },\n (table) => [\n index(\"verified_access_manifest_bundles_object_idx\").on(\n table.objectKind,\n table.objectId,\n desc(table.epoch),\n ),\n // Eviction order: oldest verification first, hash as the tiebreak.\n index(\"verified_access_manifest_bundles_eviction_idx\").on(\n table.verifiedAt,\n table.manifestHash,\n ),\n // Recent citations: an organization's newest verifications first.\n index(\"verified_access_manifest_bundles_organization_recent_idx\").on(\n table.organizationId,\n desc(table.verifiedAt),\n table.manifestHash,\n ),\n ],\n);\n\nexport const verifiedManifestBundleTables: ReadonlyArray<SqlTableSchema> = [\n defineSqlTableSchema(verifiedAccessManifestBundles),\n];\n"]}
@@ -11,7 +11,7 @@ import { type UploadMultipartBlobPartBytesRequest } from "./routes/blobs/get.js"
11
11
  import { type ContainerKekLogOptions } from "./routes/containers/reads.js";
12
12
  import { type DocumentPurgeProofOptions } from "./routes/documents/mutations.js";
13
13
  import type { HttpMethod, ListDocumentEditAttributionRangesOptions, RequestResult, RequestResultOptions } from "./types.js";
14
- import type { VerifiedManifestCitations } from "./verifiedManifestCitations.js";
14
+ import type { VerifiedManifestCitations, WriterProjectionListing } from "./verifiedManifestCitations.js";
15
15
  type ExpiredHandler = () => boolean | Promise<boolean>;
16
16
  type PaymentRequiredHandler = (organizationId: string | null) => void;
17
17
  export declare class ApiClient {
@@ -2827,7 +2827,8 @@ export declare class ApiClient {
2827
2827
  } | undefined;
2828
2828
  } | null>;
2829
2829
  createDocumentResult(input: DocumentCreateRequest, options?: RequestResultOptions): Promise<RequestResult<DocumentCreateResponse>>;
2830
- getContainerWriterProjection(containerId: string): Promise<{
2830
+ /** `listing` only picks which held bytes an unstored container cites. */
2831
+ getContainerWriterProjection(containerId: string, listing?: WriterProjectionListing): Promise<{
2831
2832
  policyEvidence: {
2832
2833
  organization: {
2833
2834
  head: {
@@ -435,8 +435,9 @@ export class ApiClient {
435
435
  createDocumentResult(input, options = {}) {
436
436
  return this.requestResult(documentCreate.path, documentCreate.isResponse, documentCreate.method, JSON.stringify(input), options, createDocumentOperation);
437
437
  }
438
- getContainerWriterProjection(containerId) {
439
- return cachedRequest(this.containerWriterProjectionRequestsByContainerId, containerId, () => this.request(containerWriterProjection.path(containerId), containerWriterProjection.isResponseFor(containerId), containerWriterProjection.method, undefined, undefined, getContainerWriterProjectionOperation));
438
+ /** `listing` only picks which held bytes an unstored container cites. */
439
+ getContainerWriterProjection(containerId, listing) {
440
+ return cachedRequest(this.containerWriterProjectionRequestsByContainerId, containerId, () => this.request(containerWriterProjection.path(containerId), containerWriterProjection.isResponseFor(containerId), containerWriterProjection.method, undefined, listing ? { projectionListing: listing } : undefined, getContainerWriterProjectionOperation));
440
441
  }
441
442
  getContainerReplacementAuthorizationsResult(containerId, replacesOrganizationId) {
442
443
  return this.transport.requestResult(getContainerReplacementAuthorizationsOperation, { params: { containerId, replacesOrganizationId } }, { reportErrors: false });