@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.
- package/data/keyingProjectionVerification/checkpoints/checkpointContext.d.ts +8 -1
- package/data/keyingProjectionVerification/checkpoints/checkpointContext.js +15 -0
- package/data/keyingProjectionVerification/checkpoints/checkpointContext.js.map +1 -1
- package/data/keyingProjectionVerification/container/containerProjectionVerification.js +3 -1
- package/data/keyingProjectionVerification/container/containerProjectionVerification.js.map +1 -1
- package/data/keyingProjectionVerification/document/documentProjectionVerification.js +2 -1
- package/data/keyingProjectionVerification/document/documentProjectionVerification.js.map +1 -1
- package/data/persistence/verifiedCitations/citationScope.js +121 -0
- package/data/persistence/verifiedCitations/citationScope.js.map +1 -0
- package/data/persistence/verifiedCitations/kekLevelCitations.d.ts +33 -0
- package/data/persistence/verifiedCitations/kekLevelCitations.js +44 -0
- package/data/persistence/verifiedCitations/kekLevelCitations.js.map +1 -0
- package/data/persistence/verifiedManifestBundlePersistence.d.ts +17 -12
- package/data/persistence/verifiedManifestBundlePersistence.js +75 -104
- package/data/persistence/verifiedManifestBundlePersistence.js.map +1 -1
- package/data/sqlite/schema/verifiedManifestBundleSchema.d.ts +7 -2
- package/data/sqlite/schema/verifiedManifestBundleSchema.js +7 -2
- package/data/sqlite/schema/verifiedManifestBundleSchema.js.map +1 -1
- package/internal/api-client/ApiClient.d.ts +3 -2
- package/internal/api-client/ApiClient.js +3 -2
- package/internal/api-client/ApiClient.js.map +1 -1
- package/internal/api-client/index.d.ts +1 -1
- package/internal/api-client/index.js.map +1 -1
- package/internal/api-client/types.d.ts +7 -0
- package/internal/api-client/types.js.map +1 -1
- package/internal/api-client/verifiedManifestCitations.d.ts +26 -9
- package/internal/api-client/verifiedManifestCitations.js +44 -25
- package/internal/api-client/verifiedManifestCitations.js.map +1 -1
- package/internal/api-client/writerProjectionRequests.js +21 -10
- package/internal/api-client/writerProjectionRequests.js.map +1 -1
- package/internal/crypto/index.d.ts +1 -1
- package/internal/crypto/index.js +1 -1
- package/internal/crypto/index.js.map +1 -1
- package/internal/crypto/keying/canonicalJsonGuard.d.ts +7 -0
- package/internal/crypto/keying/canonicalJsonGuard.js +20 -0
- package/internal/crypto/keying/canonicalJsonGuard.js.map +1 -1
- package/internal/crypto/keying/index.d.ts +1 -1
- package/internal/crypto/keying/index.js +1 -1
- package/internal/crypto/keying/index.js.map +1 -1
- package/internal/validators/knownManifestsRefinements.js +1 -1
- package/internal/validators/knownManifestsRefinements.js.map +1 -1
- package/internal/validators/operation/writerProjections.d.ts +42 -18
- package/internal/validators/response/container.d.ts +137 -16
- package/internal/validators/response/container.js +23 -2
- package/internal/validators/response/container.js.map +1 -1
- package/internal/validators/response/documentMutation.d.ts +20 -8
- package/internal/validators/response/index.d.ts +2 -2
- package/internal/validators/response/index.js +2 -2
- package/internal/validators/response/index.js.map +1 -1
- package/internal/validators/response/writerProjectionWire.d.ts +19 -8
- package/internal/validators/response/writerProjectionWire.js +78 -20
- package/internal/validators/response/writerProjectionWire.js.map +1 -1
- package/internal/validators/util/index.d.ts +1 -1
- package/internal/validators/util/index.js +1 -1
- package/internal/validators/util/index.js.map +1 -1
- package/internal/validators/util/knownManifestWire.d.ts +13 -3
- package/internal/validators/util/knownManifestWire.js +37 -10
- package/internal/validators/util/knownManifestWire.js.map +1 -1
- package/package.json +1 -1
- package/workflows/container-contents/remoteHydration/destination/destinationPrefetch.js +10 -5
- package/workflows/container-contents/remoteHydration/destination/destinationPrefetch.js.map +1 -1
- package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerPageHydration.js +4 -6
- package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerPageHydration.js.map +1 -1
- package/workflows/container-contents/remoteHydration/types.d.ts +2 -2
- 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
|
-
|
|
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
|
|
24
|
-
* digests are computed here, before the checkpoint
|
|
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
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* of its organization, since a
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* Damaged rows are
|
|
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<
|
|
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
|
|
4
|
-
import { and, asc, desc, eq, inArray, sql } from "drizzle-orm";
|
|
5
|
-
import { containerTables,
|
|
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
|
|
47
|
-
* digests are computed here, before the checkpoint
|
|
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
|
-
*
|
|
246
|
-
*
|
|
247
|
-
* of its organization, since a
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
* Damaged rows are
|
|
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":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAEL,kCAAkC,GACnC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EACL,eAAe,EACf,iCAAiC,GAClC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,6BAA6B,IAAI,OAAO,EACxC,4BAA4B,GAC7B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAGL,iCAAiC,GAClC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAgB,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAEL,YAAY,EACZ,kBAAkB,GACnB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,qBAAqB,EACrB,WAAW,EACX,kBAAkB,GAEnB,MAAM,uCAAuC,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC;AAClD,MAAM,eAAe,GAAG,GAAG,CAAC;AAe5B,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,aAAa,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,uBAAuB,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAE/D,SAAS,MAAM,CAAI,MAAoB,EAAE,IAAY;IACnD,MAAM,MAAM,GAAU,EAAE,CAAC;IACzB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,IAAI,IAAI;QACtD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC;IACjD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,OAAO,CAAI,MAAoB;IACtC,OAAO,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AACzC,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,EAAgC;IAEhC,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,CAAQ,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9E,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,6BAA6B,CAAC;IACjE,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO;IACxB,MAAM,OAAO,GAAG,MAAM,EAAE;SACrB,MAAM,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;SAC9C,IAAI,CAAC,OAAO,CAAC;SACb,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;SAC3D,KAAK,CAAC,MAAM,CAAC,CAAC;IACjB,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACrE,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;AAC/E,CAAC;AAQD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,OAA0C;IAE1C,MAAM,aAAa,GAAqB,EAAE,CAAC;IAC3C,MAAM,YAAY,GAAuB,EAAE,CAAC;IAC5C,KAAK,MAAM,KAAK,IAAI,OAAO;QACzB,IAAI,UAAU,IAAI,KAAK;YAAE,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;YAC7C,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjC,yEAAyE;IACzE,MAAM,SAAS,GAAG,IAAI,GAAG,EAAqC,CAAC;IAC/D,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAChD,IAAI,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC;YAC3C,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,MAAM,UAAU,CAAC,aAAa,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,UAAU,CACjB,OAAkC;IAElC,MAAM,MAAM,GAAG,IAAI,GAAG,CACpB,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAC3D,CAAC;IACF,OAAO,OAAO,CAAC,GAAG,CAChB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE;QACxD,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC1C,OAAO;YACL,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,cAAc,EAAE,UAAU,CAAC,cAAc;YACzC,UAAU,EAAE,UAAU,CAAC,UAAU;YACjC,QAAQ,EAAE,UAAU,CAAC,QAAQ;YAC7B,KAAK,EAAE,UAAU,CAAC,KAAK;YACvB,UAAU;YACV,YAAY,EAAE,MAAM,SAAS,CAAC,UAAU,CAAC;SAC1C,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,yCAAyC,CAC7D,EAAgC,EAChC,IAA0C,EAC1C,UAAkB;IAElB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAC9B,MAAM,MAAM,GAAG,MAAM,aAAa,CAChC,EAAE,EACF,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,CACpC,CAAC;IACF,4DAA4D;IAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAC3B,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,GAAG,CAAC,YAAY,CAC3D,CAAC;IACF,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACnE,MAAM,EAAE;aACL,MAAM,CAAC,OAAO,CAAC;aACf,GAAG,CAAC,EAAE,UAAU,EAAE,CAAC;aACnB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;aAC3C,GAAG,EAAE,CAAC;IACX,2EAA2E;IAC3E,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,IAAI;SACD,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,GAAG,CAAC,YAAY,CAAC;SAClE,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,EAAE,GAAG,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC,CAC5D,CAAC;IACF,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,uBAAuB,CAAC;QACxE,MAAM,EAAE;aACL,MAAM,CAAC,OAAO,CAAC;aACf,MAAM,CAAC,KAAK,CAAC;aACb,kBAAkB,CAAC;YAClB,MAAM,EAAE,OAAO,CAAC,YAAY;YAC5B,GAAG,EAAE;gBACH,cAAc,EAAE,aAAa,CAAC,OAAO,CAAC,cAAc,CAAC;gBACrD,UAAU,EAAE,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC;gBAC7C,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC;gBACzC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC;gBACnC,UAAU,EAAE,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC;gBAC7C,YAAY,EAAE,aAAa,CAAC,OAAO,CAAC,YAAY,CAAC;gBACjD,UAAU,EAAE,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC;aAC9C;SACF,CAAC;aACD,GAAG,EAAE,CAAC;IACX,MAAM,uBAAuB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACxC,MAAM,kBAAkB,CAAC,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,uBAAuB,CACpC,EAAgC,EAChC,IAA0C;IAE1C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,KAAK,qBAAqB,CAAC,CAAC;IAC9E,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;QACjC,MAAM,EAAE;aACL,MAAM,CAAC,OAAO,CAAC;aACf,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC7C,OAAO,CACL,OAAO,CAAC,QAAQ,EAChB,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CACjC,EACD,UAAU,CACR,OAAO,CAAC,YAAY,EACpB,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,CACrC,CACF,CACF;aACA,GAAG,EAAE,CAAC;AACb,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,EAAgC,EAChC,cAAiC;IAEjC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,MAAM,EAAE;aAClB,MAAM,CAAC;YACN,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,YAAY,EAAE,OAAO,CAAC,YAAY;SACnC,CAAC;aACD,IAAI,CAAC,OAAO,CAAC;aACb,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/C,KAAK,MAAM,GAAG,IAAI,IAAI;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAQD,MAAM,aAAa,GAAG;IACpB,YAAY,EAAE,OAAO,CAAC,YAAY;IAClC,UAAU,EAAE,OAAO,CAAC,UAAU;IAC9B,YAAY,EAAE,OAAO,CAAC,YAAY;CACnC,CAAC;AAEF,KAAK,UAAU,gBAAgB,CAC7B,GAAoB;IAEpB,IAAI,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,GAAG,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IACxE,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACnD,OAAO,kCAAkC,CAAC,MAAM,CAAC;YAC/C,MAAM,CAAC,YAAY,KAAK,GAAG,CAAC,YAAY;YACxC,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,IAAI,CAAC;IACX,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CACjB,EAAwB,EACxB,cAAsB,EACtB,MAAmB,EACnB,KAAa;IAEb,OAAO,EAAE;SACN,MAAM,CAAC,aAAa,CAAC;SACrB,IAAI,CAAC,OAAO,CAAC;SACb,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,EAC1C,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,EACnC,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,CAChC,CACF;SACA,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;SAC5B,KAAK,CAAC,KAAK,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,UAAU,CACjB,EAAwB,EACxB,cAAsB,EACtB,KAAa;IAEb,OAAO,EAAE;SACN,MAAM,CAAC,aAAa,CAAC;SACrB,IAAI,CAAC,OAAO,CAAC;SACb,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,EAC1C,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAC9C,CACF;SACA,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;SAC5D,KAAK,CAAC,KAAK,CAAC,CAAC;AAClB,CAAC;AAED,yEAAyE;AACzE,KAAK,UAAU,gBAAgB,CAC7B,EAAwB,EACxB,cAAsB,EACtB,OAA+B;IAE/B,MAAM,YAAY,GAAG,OAAO;SACzB,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,WAAW,CAAC;SAC/C,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC9B,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACzC,MAAM,IAAI,GAAG,MAAM,EAAE;SAClB,MAAM,CAAC,EAAE,GAAG,aAAa,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;SACxD,IAAI,CAAC,OAAO,CAAC;SACb,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,EAC1C,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC7C,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,CACxC,CACF;SACA,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,IAAI,GAAG,EAA2B,CAAC;IAClD,KAAK,MAAM,GAAG,IAAI,IAAI;QACpB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC/D,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,OAAgB,EAChB,MAA8B,EAC9B,KAAK,GAAG,yBAAyB;IAEjC,MAAM,eAAe,CAAC,OAAO,EAAE;QAC7B,GAAG,4BAA4B;QAC/B,GAAG,eAAe;QAClB,GAAG,iCAAiC;KACrC,CAAC,CAAC;IACH,MAAM,EAAE,EAAE,EAAE,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkC,CAAC;IACxD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,KAAK,EAAE,IAAgC,EAAE,EAAE;QACzD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;gBAAE,SAAS;YACjE,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,MAAM;gBAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;;gBAC3C,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAC/C,MAAM,cAAc,GAAG,MAAM,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACrE,IAAI,cAAc,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACvC,KAAK,MAAM,GAAG,IAAI,MAAM,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,CAAC;QACtE,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK;YAAE,MAAM;QAC/B,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,KAAK;YAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;;YACzC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACtC,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,OAAO;QAC1B,IAAI,KAAK,CAAC,IAAI,GAAG,KAAK;YACpB,MAAM,OAAO,CACX,MAAM,UAAU,CAAC,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CACjE,CAAC;IACN,IAAI,KAAK,CAAC,IAAI,GAAG,KAAK;QACpB,MAAM,OAAO,CAAC,MAAM,UAAU,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1E,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QACpB,MAAM,iCAAiC,CAAC,OAAO,CAAC;aAC7C,WAAW,CAAC,CAAC,MAAM,EAAE,EAAE,CACtB,MAAM;aACH,MAAM,CAAC,OAAO,CAAC;aACf,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;aAC7C,GAAG,EAAE,CACT;aACA,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC5B,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,+BAA+B,CAC7C,cAAoC;IAEpC,OAAO;QACL,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YACrB,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;YACjC,OAAO,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,CAAC;KACF,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,4CAA4C,CAChE,EAAgC,EAChC,cAAsB;IAEtB,MAAM,EAAE;SACL,MAAM,CAAC,OAAO,CAAC;SACf,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;SACjD,GAAG,EAAE,CAAC;AACX,CAAC","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`:
|
|
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
|
|
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`:
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
439
|
-
|
|
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 });
|