sealkeep 0.9.0 → 0.11.0
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/ARCHITECTURE.md +163 -14
- package/CHANGELOG.md +223 -1
- package/CONTROL_PLANE.md +2 -2
- package/LICENSE +1 -1
- package/README.md +108 -22
- package/THIRD_PARTY.md +2 -2
- package/THREAT_MODEL.md +23 -4
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/chunk-access.d.ts +26 -4
- package/dist/packages/sealkeep-crypto/src/chunk-access.js +219 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.d.ts +11 -1
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.js +58 -21
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.d.ts +2 -2
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.d.ts +1 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.js +1 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.js +4 -4
- package/dist/packages/sealkeep-crypto/src/squeeze.d.ts +6 -0
- package/dist/packages/sealkeep-crypto/src/squeeze.js +39 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.d.ts +36 -3
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.js +196 -37
- package/dist/site/index.html +1808 -1904
- package/dist/site/llms.txt +67 -0
- package/dist/site/trust/architecture-data-flow.html +53 -0
- package/dist/site/trust/audit-roadmap.html +37 -0
- package/dist/site/trust/deployment-responsibility.html +11 -0
- package/dist/site/trust/dpa-sample.html +30 -0
- package/dist/site/trust/release-provenance.html +21 -0
- package/dist/site/trust/subprocessors.html +15 -0
- package/dist/site/trust/threat-model.html +34 -0
- package/dist/site/trust/toms.html +41 -0
- package/dist/site/trust-document.css +32 -0
- package/dist/site/trust.html +73 -0
- package/dist/site/visual/assets/index-8Nxnkc7o.js +207 -0
- package/dist/site/visual/assets/index-oxLy1bpy.css +1 -0
- package/dist/site/visual/index.html +18 -0
- package/dist/site.zip +0 -0
- package/dist/src/activity.d.ts +9 -0
- package/dist/src/activity.js +90 -1
- package/dist/src/adapters.d.ts +175 -5
- package/dist/src/adapters.js +961 -73
- package/dist/src/agent-context.d.ts +135 -0
- package/dist/src/agent-context.js +1059 -0
- package/dist/src/archive-copies.d.ts +47 -0
- package/dist/src/archive-copies.js +179 -0
- package/dist/src/audit.d.ts +1 -1
- package/dist/src/audit.js +29 -4
- package/dist/src/autopilot.d.ts +52 -7
- package/dist/src/autopilot.js +143 -25
- package/dist/src/background-bandwidth.d.ts +46 -0
- package/dist/src/background-bandwidth.js +301 -0
- package/dist/src/background-cpu.d.ts +82 -0
- package/dist/src/background-cpu.js +212 -0
- package/dist/src/background-worker-error.d.ts +12 -0
- package/dist/src/background-worker-error.js +18 -0
- package/dist/src/branding.d.ts +24 -1
- package/dist/src/branding.js +31 -1
- package/dist/src/bridge.d.ts +233 -0
- package/dist/src/bridge.js +604 -0
- package/dist/src/byte-stream.d.ts +91 -0
- package/dist/src/byte-stream.js +385 -0
- package/dist/src/chunk-store.d.ts +41 -8
- package/dist/src/chunk-store.js +161 -65
- package/dist/src/cli.js +1698 -163
- package/dist/src/cloud.d.ts +841 -31
- package/dist/src/cloud.js +3196 -277
- package/dist/src/context-background.d.ts +37 -0
- package/dist/src/context-background.js +309 -0
- package/dist/src/context-drain-child.d.ts +1 -0
- package/dist/src/context-drain-child.js +98 -0
- package/dist/src/context-reader.d.ts +118 -0
- package/dist/src/context-reader.js +447 -0
- package/dist/src/control-plane/auth.d.ts +32 -4
- package/dist/src/control-plane/auth.js +85 -24
- package/dist/src/control-plane/server.js +19 -6
- package/dist/src/control-plane.d.ts +17 -1
- package/dist/src/control-plane.js +32 -6
- package/dist/src/crypto.d.ts +1 -1
- package/dist/src/crypto.js +5 -5
- package/dist/src/daemon-lease.d.ts +70 -0
- package/dist/src/daemon-lease.js +420 -0
- package/dist/src/daemon.d.ts +94 -1
- package/dist/src/daemon.js +1082 -105
- package/dist/src/darwin-service-policy.d.ts +41 -0
- package/dist/src/darwin-service-policy.js +60 -0
- package/dist/src/dashboard-cli.js +15 -15
- package/dist/src/device-authorization.d.ts +37 -0
- package/dist/src/device-authorization.js +199 -0
- package/dist/src/device-enrollment.d.ts +91 -0
- package/dist/src/device-enrollment.js +349 -0
- package/dist/src/disk.d.ts +17 -12
- package/dist/src/disk.js +43 -17
- package/dist/src/doctor.d.ts +35 -1
- package/dist/src/doctor.js +316 -41
- package/dist/src/durable-ticket-lock.d.ts +24 -0
- package/dist/src/durable-ticket-lock.js +232 -0
- package/dist/src/enroll.d.ts +1 -1
- package/dist/src/enroll.js +13 -7
- package/dist/src/env.d.ts +10 -1
- package/dist/src/env.js +11 -3
- package/dist/src/errors.d.ts +8 -8
- package/dist/src/errors.js +6 -6
- package/dist/src/flush.d.ts +12 -0
- package/dist/src/flush.js +37 -0
- package/dist/src/heartbeat.d.ts +86 -12
- package/dist/src/heartbeat.js +415 -29
- package/dist/src/index-background-watchdog.d.ts +1 -0
- package/dist/src/index-background-watchdog.js +94 -0
- package/dist/src/index-background-work.d.ts +21 -0
- package/dist/src/index-background-work.js +25 -0
- package/dist/src/index-background.d.ts +64 -0
- package/dist/src/index-background.js +394 -0
- package/dist/src/index-build-child.d.ts +1 -0
- package/dist/src/index-build-child.js +109 -0
- package/dist/src/index-manifest.d.ts +52 -0
- package/dist/src/index-manifest.js +444 -0
- package/dist/src/index-publication-proof.d.ts +84 -0
- package/dist/src/index-publication-proof.js +380 -0
- package/dist/src/index-publication-state.d.ts +149 -0
- package/dist/src/index-publication-state.js +696 -0
- package/dist/src/index-publication-verifier.d.ts +89 -0
- package/dist/src/index-publication-verifier.js +341 -0
- package/dist/src/index-publish.d.ts +62 -0
- package/dist/src/index-publish.js +540 -0
- package/dist/src/index-scratch-cleanup.d.ts +19 -0
- package/dist/src/index-scratch-cleanup.js +166 -0
- package/dist/src/index-segment-types.d.ts +132 -0
- package/dist/src/index-segment-types.js +21 -0
- package/dist/src/index-segments.d.ts +9 -0
- package/dist/src/index-segments.js +516 -0
- package/dist/src/index-store.d.ts +123 -0
- package/dist/src/index-store.js +495 -0
- package/dist/src/index-sync.d.ts +91 -15
- package/dist/src/index-sync.js +286 -53
- package/dist/src/index-upgrade-publication.d.ts +30 -0
- package/dist/src/index-upgrade-publication.js +179 -0
- package/dist/src/integration-manager.d.ts +32 -0
- package/dist/src/integration-manager.js +394 -0
- package/dist/src/leakscan.js +1 -1
- package/dist/src/local-api.d.ts +56 -15
- package/dist/src/local-api.js +4979 -541
- package/dist/src/machine-settings.d.ts +51 -0
- package/dist/src/machine-settings.js +166 -0
- package/dist/src/managed-chunks.d.ts +5 -2
- package/dist/src/managed-chunks.js +14 -14
- package/dist/src/mcp-install.d.ts +11 -9
- package/dist/src/mcp-install.js +73 -28
- package/dist/src/mcp-workspace.d.ts +18 -0
- package/dist/src/mcp-workspace.js +50 -0
- package/dist/src/mcp.js +294 -25
- package/dist/src/migrate.js +27 -21
- package/dist/src/notify.d.ts +1 -1
- package/dist/src/notify.js +5 -5
- package/dist/src/offload.d.ts +201 -14
- package/dist/src/offload.js +1848 -140
- package/dist/src/onboarding.d.ts +8 -1
- package/dist/src/onboarding.js +4 -4
- package/dist/src/packages.d.ts +2 -2
- package/dist/src/packages.js +10 -2
- package/dist/src/passkey.d.ts +0 -1
- package/dist/src/passkey.js +2 -7
- package/dist/src/password-lock.d.ts +2 -2
- package/dist/src/password-lock.js +6 -6
- package/dist/src/paths.d.ts +2 -0
- package/dist/src/paths.js +2 -0
- package/dist/src/presence.d.ts +86 -0
- package/dist/src/presence.js +240 -0
- package/dist/src/progress-deadline.d.ts +21 -0
- package/dist/src/progress-deadline.js +91 -0
- package/dist/src/project-repair.d.ts +55 -0
- package/dist/src/project-repair.js +131 -0
- package/dist/src/providers/gcs.d.ts +28 -7
- package/dist/src/providers/gcs.js +35 -24
- package/dist/src/providers/gdrive.d.ts +71 -8
- package/dist/src/providers/gdrive.js +223 -50
- package/dist/src/providers/index.d.ts +11 -3
- package/dist/src/providers/index.js +30 -10
- package/dist/src/providers/s3.d.ts +30 -8
- package/dist/src/providers/s3.js +41 -30
- package/dist/src/providers/safe-storage-fetch.d.ts +12 -0
- package/dist/src/providers/safe-storage-fetch.js +72 -0
- package/dist/src/queue.d.ts +136 -19
- package/dist/src/queue.js +862 -96
- package/dist/src/reclaim-transaction.d.ts +156 -0
- package/dist/src/reclaim-transaction.js +1027 -0
- package/dist/src/recovery-codes.d.ts +32 -0
- package/dist/src/recovery-codes.js +338 -0
- package/dist/src/recovery.js +12 -9
- package/dist/src/rehydrate.d.ts +25 -22
- package/dist/src/rehydrate.js +319 -23
- package/dist/src/restore.d.ts +57 -4
- package/dist/src/restore.js +272 -36
- package/dist/src/resume-stub.d.ts +92 -0
- package/dist/src/resume-stub.js +417 -0
- package/dist/src/retention.d.ts +98 -7
- package/dist/src/retention.js +1066 -62
- package/dist/src/rotate.js +3 -3
- package/dist/src/search.d.ts +566 -8
- package/dist/src/search.js +5897 -290
- package/dist/src/secrets.d.ts +51 -7
- package/dist/src/secrets.js +316 -24
- package/dist/src/service.d.ts +49 -11
- package/dist/src/service.js +776 -35
- package/dist/src/share.js +3 -3
- package/dist/src/shared-spaces.d.ts +98 -0
- package/dist/src/shared-spaces.js +214 -0
- package/dist/src/source-reader.d.ts +73 -0
- package/dist/src/source-reader.js +715 -0
- package/dist/src/spool.d.ts +1 -1
- package/dist/src/spool.js +1 -1
- package/dist/src/start-tui.js +2 -1
- package/dist/src/start.js +2 -2
- package/dist/src/storage-endpoint.d.ts +21 -0
- package/dist/src/storage-endpoint.js +122 -0
- package/dist/src/storage-setup.js +12 -12
- package/dist/src/storage-targets.d.ts +109 -6
- package/dist/src/storage-targets.js +975 -67
- package/dist/src/stream-to-cloud.d.ts +5 -1
- package/dist/src/stream-to-cloud.js +34 -14
- package/dist/src/sync-rules.d.ts +31 -6
- package/dist/src/sync-rules.js +153 -14
- package/dist/src/team-backfill-scheduling.d.ts +8 -0
- package/dist/src/team-backfill-scheduling.js +33 -0
- package/dist/src/team-backfill.d.ts +116 -0
- package/dist/src/team-backfill.js +1429 -0
- package/dist/src/team-index-cache.d.ts +16 -0
- package/dist/src/team-index-cache.js +152 -0
- package/dist/src/team-offboarding.d.ts +38 -0
- package/dist/src/team-offboarding.js +1043 -0
- package/dist/src/team-presence.d.ts +127 -0
- package/dist/src/team-presence.js +904 -0
- package/dist/src/team-publication-policy.d.ts +20 -0
- package/dist/src/team-publication-policy.js +140 -0
- package/dist/src/team-realtime.d.ts +68 -0
- package/dist/src/team-realtime.js +816 -0
- package/dist/src/team-source-facts-cache.d.ts +23 -0
- package/dist/src/team-source-facts-cache.js +255 -0
- package/dist/src/trash.d.ts +1 -1
- package/dist/src/trash.js +2 -2
- package/dist/src/tui.js +11 -12
- package/dist/src/types.d.ts +173 -7
- package/dist/src/types.js +20 -0
- package/dist/src/ui-server.d.ts +163 -35
- package/dist/src/ui-server.js +712 -72
- package/dist/src/ui.d.ts +1 -2
- package/dist/src/ui.js +1 -2
- package/dist/src/upload.d.ts +27 -0
- package/dist/src/upload.js +383 -43
- package/dist/src/vault.d.ts +226 -30
- package/dist/src/vault.js +1776 -192
- package/dist/src/watcher.d.ts +7 -1
- package/dist/src/watcher.js +198 -55
- package/dist/src/worker.d.ts +27 -3
- package/dist/src/worker.js +274 -55
- package/package.json +33 -12
- package/scripts/native-reboot-rehearsal.mjs +90 -0
- package/web/app.js +6032 -343
- package/web/bootstrap.js +17 -0
- package/web/index.html +255 -57
- package/web/rail.js +317 -40
- package/web/retention.html +2 -2
- package/web/rules-view.js +188 -16
- package/web/sessions-view.js +485 -62
- package/web/sessions.html +2 -2
- package/web/setup-api.js +152 -29
- package/web/setup-logic.js +68 -9
- package/web/setup.html +113 -44
- package/web/setup.js +604 -71
- package/web/style.css +513 -98
- package/dist/packages/vaultline-crypto/src/chunk-access.js +0 -93
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.js +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.js +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.js +0 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export const INDEX_PUBLICATION_DIGEST_ALGORITHM = "sha256";
|
|
3
|
+
export const INDEX_PUBLICATION_MAX_POSTINGS_PER_TOKEN = 256;
|
|
4
|
+
export class IndexPublicationProofError extends Error {
|
|
5
|
+
code = "INDEX_PUBLICATION_PROOF_INVALID";
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "IndexPublicationProofError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
12
|
+
const lexical = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
13
|
+
function invalid(message) {
|
|
14
|
+
throw new IndexPublicationProofError(message);
|
|
15
|
+
}
|
|
16
|
+
function record(value, label) {
|
|
17
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
18
|
+
return invalid(`${label} must be an object`);
|
|
19
|
+
}
|
|
20
|
+
const prototype = Object.getPrototypeOf(value);
|
|
21
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
22
|
+
return invalid(`${label} must be a plain JSON object`);
|
|
23
|
+
}
|
|
24
|
+
if (Object.getOwnPropertySymbols(value).length !== 0) {
|
|
25
|
+
return invalid(`${label} must not contain symbol keys`);
|
|
26
|
+
}
|
|
27
|
+
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
|
|
28
|
+
if (descriptor.enumerable && !("value" in descriptor)) {
|
|
29
|
+
return invalid(`${label}.${key} must not be an accessor`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A compact canonical JSON-value encoding used only inside the digest.
|
|
36
|
+
*
|
|
37
|
+
* It sorts every object recursively and rejects values JSON cannot preserve.
|
|
38
|
+
* Type tags retain distinctions such as string "1" versus number 1 and -0
|
|
39
|
+
* versus 0. Array holes are rejected instead of silently becoming null.
|
|
40
|
+
*/
|
|
41
|
+
function canonicalValue(value, ancestors = new Set()) {
|
|
42
|
+
if (value === null)
|
|
43
|
+
return "n";
|
|
44
|
+
if (typeof value === "string")
|
|
45
|
+
return `s${JSON.stringify(value)}`;
|
|
46
|
+
if (typeof value === "boolean")
|
|
47
|
+
return value ? "b1" : "b0";
|
|
48
|
+
if (typeof value === "number") {
|
|
49
|
+
if (!Number.isFinite(value))
|
|
50
|
+
return invalid("index metadata contains a non-finite number");
|
|
51
|
+
return `d${Object.is(value, -0) ? "-0" : JSON.stringify(value)}`;
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(value)) {
|
|
54
|
+
if (ancestors.has(value))
|
|
55
|
+
return invalid("index metadata contains a cycle");
|
|
56
|
+
ancestors.add(value);
|
|
57
|
+
const entries = [];
|
|
58
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
59
|
+
if (!own(value, index))
|
|
60
|
+
return invalid("index metadata contains a sparse array");
|
|
61
|
+
entries.push(canonicalValue(value[index], ancestors));
|
|
62
|
+
}
|
|
63
|
+
ancestors.delete(value);
|
|
64
|
+
return `a[${entries.join(",")}]`;
|
|
65
|
+
}
|
|
66
|
+
if (typeof value !== "object") {
|
|
67
|
+
return invalid(`index metadata contains unsupported ${typeof value}`);
|
|
68
|
+
}
|
|
69
|
+
const object = record(value, "index metadata");
|
|
70
|
+
if (ancestors.has(object))
|
|
71
|
+
return invalid("index metadata contains a cycle");
|
|
72
|
+
ancestors.add(object);
|
|
73
|
+
const encoded = Object.keys(object).sort(lexical).map((key) => {
|
|
74
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
75
|
+
if (!descriptor || !("value" in descriptor))
|
|
76
|
+
return invalid(`index metadata.${key} must be a data property`);
|
|
77
|
+
return `${JSON.stringify(key)}:${canonicalValue(descriptor.value, ancestors)}`;
|
|
78
|
+
});
|
|
79
|
+
ancestors.delete(object);
|
|
80
|
+
return `o{${encoded.join(",")}}`;
|
|
81
|
+
}
|
|
82
|
+
/** Length framing makes adjacent values unambiguous without constructing a
|
|
83
|
+
* vault-sized intermediate string. */
|
|
84
|
+
function frame(hash, label, value) {
|
|
85
|
+
hash.update(label, "utf8");
|
|
86
|
+
hash.update("\0", "utf8");
|
|
87
|
+
hash.update(String(Buffer.byteLength(value, "utf8")), "utf8");
|
|
88
|
+
hash.update(":", "utf8");
|
|
89
|
+
hash.update(value, "utf8");
|
|
90
|
+
}
|
|
91
|
+
function archiveReference(value) {
|
|
92
|
+
if (!value)
|
|
93
|
+
return invalid("an archive reference is empty");
|
|
94
|
+
// `@cN` is the existing posting delimiter. Current account ids and team
|
|
95
|
+
// shareRefs are UUIDs; reserving it prevents an ambiguous ownership proof.
|
|
96
|
+
if (value.includes("@"))
|
|
97
|
+
return invalid(`archive reference ${JSON.stringify(value)} contains the reserved @ delimiter`);
|
|
98
|
+
if (value === "__proto__")
|
|
99
|
+
return invalid("archive reference __proto__ is unsafe in a coverage map");
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
function postingOwner(posting, archiveRefs) {
|
|
103
|
+
if (!posting)
|
|
104
|
+
return invalid("an index posting is empty");
|
|
105
|
+
const delimiter = posting.indexOf("@");
|
|
106
|
+
const owner = delimiter === -1 ? posting : posting.slice(0, delimiter);
|
|
107
|
+
if (delimiter !== -1 && !/^@c(?:0|[1-9]\d*)$/.test(posting.slice(delimiter))) {
|
|
108
|
+
return invalid(`index posting ${JSON.stringify(posting)} does not use the archiveRef@cN format`);
|
|
109
|
+
}
|
|
110
|
+
if (!archiveRefs.has(owner)) {
|
|
111
|
+
return invalid(`index posting ${JSON.stringify(posting)} refers to an absent archive`);
|
|
112
|
+
}
|
|
113
|
+
return owner;
|
|
114
|
+
}
|
|
115
|
+
function archiveTime(meta) {
|
|
116
|
+
if (!meta)
|
|
117
|
+
return undefined;
|
|
118
|
+
for (const field of ["startedAt", "endedAt", "archivedAt"]) {
|
|
119
|
+
const value = meta[field];
|
|
120
|
+
if (value !== undefined && value !== null && typeof value !== "string") {
|
|
121
|
+
return invalid(`index metadata ${field} must be a string when present`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Match the serializer's nullish fallback exactly. In particular an explicit
|
|
125
|
+
// empty startedAt wins over endedAt and then behaves as an undated posting.
|
|
126
|
+
const value = meta.startedAt ?? meta.endedAt ?? meta.archivedAt;
|
|
127
|
+
return typeof value === "string" && value ? value : undefined;
|
|
128
|
+
}
|
|
129
|
+
/** Mirrors `serializedIndexRows`: timestamped postings are chronological,
|
|
130
|
+
* undated/tied postings retain their input order, then only the newest 256 are
|
|
131
|
+
* present in the actual encrypted JSONL generation. */
|
|
132
|
+
function effectivePostings(entries, archiveRefs, metadata) {
|
|
133
|
+
const ordered = entries.map((value, position) => {
|
|
134
|
+
if (typeof value !== "string")
|
|
135
|
+
return invalid("every index posting must be a string");
|
|
136
|
+
const owner = postingOwner(value, archiveRefs);
|
|
137
|
+
const at = archiveTime(metadata.get(owner));
|
|
138
|
+
return { posting: value, owner, position, ...(at ? { at } : {}) };
|
|
139
|
+
}).sort((left, right) => {
|
|
140
|
+
if (left.at && right.at)
|
|
141
|
+
return left.at.localeCompare(right.at) || left.position - right.position;
|
|
142
|
+
if (left.at)
|
|
143
|
+
return 1;
|
|
144
|
+
if (right.at)
|
|
145
|
+
return -1;
|
|
146
|
+
return left.position - right.position;
|
|
147
|
+
});
|
|
148
|
+
return ordered.length > INDEX_PUBLICATION_MAX_POSTINGS_PER_TOKEN
|
|
149
|
+
? ordered.slice(-INDEX_PUBLICATION_MAX_POSTINGS_PER_TOKEN)
|
|
150
|
+
: ordered;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Incremental coverage for the exact rows an index checkpoint emits.
|
|
154
|
+
*
|
|
155
|
+
* The writer should call `addArchive` from its archive-row loop, `addToken`
|
|
156
|
+
* with the already chronological/capped posting array used for that exact
|
|
157
|
+
* token row, `addMetadata` from its metadata-row loop, then `finish`. Construct
|
|
158
|
+
* it with the same positive extraction policy written in the JSONL header. This
|
|
159
|
+
* avoids cloning or traversing a 600 MB index a second time, and it cannot race
|
|
160
|
+
* indexing lanes because the proof consumes the same values as the JSONL row.
|
|
161
|
+
* Token rows must be supplied in lexical order by default; enforcing that
|
|
162
|
+
* keeps new coverage independent of object insertion order. The authenticated
|
|
163
|
+
* legacy-v3 reader may explicitly preserve wire order after independently
|
|
164
|
+
* proving uniqueness with bounded state. `finish` also returns exact posting
|
|
165
|
+
* counts: zero is valid catalog coverage for a delta base, but must not be used
|
|
166
|
+
* as proof that a requested/head archive is directly searchable.
|
|
167
|
+
*/
|
|
168
|
+
export class IndexPublicationCoverageBuilder {
|
|
169
|
+
#entries = new Map();
|
|
170
|
+
#archiveRefs = new Set();
|
|
171
|
+
#phase = "archives";
|
|
172
|
+
#lastToken = null;
|
|
173
|
+
#extractionPolicy;
|
|
174
|
+
#tokenOrder;
|
|
175
|
+
constructor(options) {
|
|
176
|
+
if (!Number.isSafeInteger(options?.extractionPolicy) || options.extractionPolicy < 1) {
|
|
177
|
+
invalid("the index extraction policy must be a positive integer");
|
|
178
|
+
}
|
|
179
|
+
if (options.tokenOrder !== undefined && options.tokenOrder !== "lexical" && options.tokenOrder !== "wire") {
|
|
180
|
+
invalid("the index token order must be lexical or wire");
|
|
181
|
+
}
|
|
182
|
+
this.#extractionPolicy = options.extractionPolicy;
|
|
183
|
+
this.#tokenOrder = options.tokenOrder ?? "lexical";
|
|
184
|
+
}
|
|
185
|
+
addArchive(refValue, row) {
|
|
186
|
+
if (this.#phase !== "archives")
|
|
187
|
+
return invalid("archive rows must precede token and metadata rows");
|
|
188
|
+
const ref = archiveReference(refValue);
|
|
189
|
+
if (this.#entries.has(ref))
|
|
190
|
+
return invalid(`duplicate archive row ${JSON.stringify(ref)}`);
|
|
191
|
+
if (!Array.isArray(row))
|
|
192
|
+
return invalid(`index archive row ${JSON.stringify(ref)} must be an array`);
|
|
193
|
+
for (let position = 0; position < row.length; position += 1) {
|
|
194
|
+
if (!own(row, position) || typeof row[position] !== "string") {
|
|
195
|
+
return invalid(`index archive row ${JSON.stringify(ref)} must contain only dense string fields`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const hash = createHash(INDEX_PUBLICATION_DIGEST_ALGORITHM);
|
|
199
|
+
frame(hash, "domain", "sealkeep-index-publication-entry-v1");
|
|
200
|
+
frame(hash, "extraction-policy", String(this.#extractionPolicy));
|
|
201
|
+
frame(hash, "archive-ref", ref);
|
|
202
|
+
frame(hash, "archive-row", canonicalValue(row));
|
|
203
|
+
this.#archiveRefs.add(ref);
|
|
204
|
+
this.#entries.set(ref, { hash, postings: 0, metadataAdded: false });
|
|
205
|
+
}
|
|
206
|
+
/** Add one effective `t` row, after chronology and the 256-entry cap. */
|
|
207
|
+
addToken(token, postings) {
|
|
208
|
+
if (this.#phase === "metadata" || this.#phase === "finished") {
|
|
209
|
+
return invalid("token rows must precede metadata rows");
|
|
210
|
+
}
|
|
211
|
+
this.#phase = "tokens";
|
|
212
|
+
if (!token)
|
|
213
|
+
return invalid("an index token is empty");
|
|
214
|
+
if (this.#lastToken !== null) {
|
|
215
|
+
if (this.#lastToken === token)
|
|
216
|
+
return invalid("index token rows must be unique");
|
|
217
|
+
if (this.#tokenOrder === "lexical" && lexical(this.#lastToken, token) > 0) {
|
|
218
|
+
return invalid("index token rows must be unique and in lexical order");
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
this.#lastToken = token;
|
|
222
|
+
if (!Array.isArray(postings) || postings.length === 0) {
|
|
223
|
+
return invalid(`index token ${JSON.stringify(token)} must have at least one posting`);
|
|
224
|
+
}
|
|
225
|
+
if (postings.length > INDEX_PUBLICATION_MAX_POSTINGS_PER_TOKEN) {
|
|
226
|
+
return invalid(`index token ${JSON.stringify(token)} exceeds the effective publication cap`);
|
|
227
|
+
}
|
|
228
|
+
for (let position = 0; position < postings.length; position += 1) {
|
|
229
|
+
if (!own(postings, position))
|
|
230
|
+
return invalid(`index token ${JSON.stringify(token)} contains a sparse posting list`);
|
|
231
|
+
const posting = postings[position];
|
|
232
|
+
if (typeof posting !== "string")
|
|
233
|
+
return invalid("every index posting must be a string");
|
|
234
|
+
const owner = postingOwner(posting, this.#archiveRefs);
|
|
235
|
+
const entry = this.#entries.get(owner);
|
|
236
|
+
frame(entry.hash, "token", token);
|
|
237
|
+
frame(entry.hash, "posting", posting);
|
|
238
|
+
entry.postings += 1;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
addMetadata(refValue, value) {
|
|
242
|
+
if (this.#phase === "finished")
|
|
243
|
+
return invalid("metadata cannot be added after coverage is finalized");
|
|
244
|
+
this.#phase = "metadata";
|
|
245
|
+
const ref = archiveReference(refValue);
|
|
246
|
+
const entry = this.#entries.get(ref);
|
|
247
|
+
if (!entry)
|
|
248
|
+
return invalid(`index metadata for ${JSON.stringify(ref)} has no archive row`);
|
|
249
|
+
if (entry.metadataAdded)
|
|
250
|
+
return invalid(`duplicate index metadata for ${JSON.stringify(ref)}`);
|
|
251
|
+
const metadata = record(value, `index.meta[${JSON.stringify(ref)}]`);
|
|
252
|
+
frame(entry.hash, "meta-presence", "present");
|
|
253
|
+
frame(entry.hash, "meta", canonicalValue(metadata));
|
|
254
|
+
entry.metadataAdded = true;
|
|
255
|
+
}
|
|
256
|
+
finish() {
|
|
257
|
+
if (this.#phase === "finished")
|
|
258
|
+
return invalid("index publication coverage was already finalized");
|
|
259
|
+
this.#phase = "finished";
|
|
260
|
+
const coverage = Object.create(null);
|
|
261
|
+
const effectivePostingCounts = Object.create(null);
|
|
262
|
+
for (const ref of [...this.#entries.keys()].sort(lexical)) {
|
|
263
|
+
const entry = this.#entries.get(ref);
|
|
264
|
+
if (!entry.metadataAdded)
|
|
265
|
+
frame(entry.hash, "meta-presence", "missing");
|
|
266
|
+
frame(entry.hash, "posting-count", String(entry.postings));
|
|
267
|
+
frame(entry.hash, "end", "entry");
|
|
268
|
+
coverage[ref] = entry.hash.digest("hex");
|
|
269
|
+
effectivePostingCounts[ref] = entry.postings;
|
|
270
|
+
}
|
|
271
|
+
return { coverage, effectivePostingCounts };
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
export function createIndexPublicationCoverageBuilder(options) {
|
|
275
|
+
return new IndexPublicationCoverageBuilder(options);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Produce the exact per-archive semantic coverage committed by a search-index
|
|
279
|
+
* generation.
|
|
280
|
+
*
|
|
281
|
+
* Each SHA-256 binds the extraction policy, archive reference, its complete
|
|
282
|
+
* archive row, matching metadata (missing and `{}` differ), and every
|
|
283
|
+
* token/posting occurrence that
|
|
284
|
+
* survives the real JSONL serializer's chronological 256-posting cap. Object
|
|
285
|
+
* key order never matters. Duplicate posting occurrences do matter. Orphan or
|
|
286
|
+
* malformed data throws, so damaged input can never become positive proof.
|
|
287
|
+
* Catalog-only delta bases remain in `coverage` with a posting count of zero;
|
|
288
|
+
* callers authorizing reclaim must demand a positive count for the logical
|
|
289
|
+
* session head and exact coverage for every base needed to restore it.
|
|
290
|
+
*
|
|
291
|
+
* Runtime is O(A log A + T log T + sum(P_t log P_t)): one traversal of the
|
|
292
|
+
* sorted archive/token maps and each sorted posting list, with O(A) hash state.
|
|
293
|
+
*/
|
|
294
|
+
export function buildIndexPublicationProof(index) {
|
|
295
|
+
const archives = record(index.archives, "index.archives");
|
|
296
|
+
const tokens = record(index.tokens, "index.tokens");
|
|
297
|
+
const meta = index.meta === undefined ? null : record(index.meta, "index.meta");
|
|
298
|
+
const refs = Object.keys(archives).sort(lexical).map(archiveReference);
|
|
299
|
+
const refSet = new Set(refs);
|
|
300
|
+
const metadata = new Map();
|
|
301
|
+
if (meta) {
|
|
302
|
+
for (const ref of Object.keys(meta).sort(lexical)) {
|
|
303
|
+
if (!refSet.has(ref))
|
|
304
|
+
return invalid(`index metadata for ${JSON.stringify(ref)} has no archive row`);
|
|
305
|
+
const value = record(meta[ref], `index.meta[${JSON.stringify(ref)}]`);
|
|
306
|
+
// Validate the whole value now, before any digest could be returned.
|
|
307
|
+
canonicalValue(value);
|
|
308
|
+
metadata.set(ref, value);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const builder = createIndexPublicationCoverageBuilder({ extractionPolicy: index.extractionPolicy });
|
|
312
|
+
for (const ref of refs) {
|
|
313
|
+
const row = archives[ref];
|
|
314
|
+
if (!Array.isArray(row))
|
|
315
|
+
return invalid(`index archive row ${JSON.stringify(ref)} must be an array`);
|
|
316
|
+
builder.addArchive(ref, row);
|
|
317
|
+
}
|
|
318
|
+
for (const token of Object.keys(tokens).sort(lexical)) {
|
|
319
|
+
if (!token)
|
|
320
|
+
return invalid("an index token is empty");
|
|
321
|
+
const entries = tokens[token];
|
|
322
|
+
if (!Array.isArray(entries) || entries.length === 0) {
|
|
323
|
+
return invalid(`index token ${JSON.stringify(token)} must have at least one posting`);
|
|
324
|
+
}
|
|
325
|
+
for (let position = 0; position < entries.length; position += 1) {
|
|
326
|
+
if (!own(entries, position))
|
|
327
|
+
return invalid(`index token ${JSON.stringify(token)} contains a sparse posting list`);
|
|
328
|
+
}
|
|
329
|
+
builder.addToken(token, effectivePostings(entries, refSet, metadata).map(({ posting }) => posting));
|
|
330
|
+
}
|
|
331
|
+
for (const ref of refs) {
|
|
332
|
+
const value = metadata.get(ref);
|
|
333
|
+
if (value)
|
|
334
|
+
builder.addMetadata(ref, value);
|
|
335
|
+
}
|
|
336
|
+
return builder.finish();
|
|
337
|
+
}
|
|
338
|
+
/** Convenience for publishers that only need the canonical CAS coverage map. */
|
|
339
|
+
export function buildIndexPublicationCoverage(index) {
|
|
340
|
+
return buildIndexPublicationProof(index).coverage;
|
|
341
|
+
}
|
|
342
|
+
function validateCoverageMap(value) {
|
|
343
|
+
const source = record(value, "index publication coverage");
|
|
344
|
+
const result = Object.create(null);
|
|
345
|
+
for (const ref of Object.keys(source).sort(lexical)) {
|
|
346
|
+
archiveReference(ref);
|
|
347
|
+
const digest = source[ref];
|
|
348
|
+
if (typeof digest !== "string" || !/^[0-9a-f]{64}$/.test(digest)) {
|
|
349
|
+
return invalid(`coverage for ${JSON.stringify(ref)} is not a lowercase SHA-256 digest`);
|
|
350
|
+
}
|
|
351
|
+
result[ref] = digest;
|
|
352
|
+
}
|
|
353
|
+
return result;
|
|
354
|
+
}
|
|
355
|
+
/** Canonical, lexically sorted object JSON suitable for an opaque CAS field. */
|
|
356
|
+
export function canonicalIndexPublicationCoverageJson(value) {
|
|
357
|
+
const coverage = validateCoverageMap(value);
|
|
358
|
+
return `{${Object.keys(coverage).sort(lexical).map((ref) => `${JSON.stringify(ref)}:${JSON.stringify(coverage[ref])}`).join(",")}}`;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Parse only the canonical representation produced above. Byte-for-byte
|
|
362
|
+
* reserialization rejects duplicate JSON keys as well as ambiguous whitespace
|
|
363
|
+
* or ordering, instead of letting JSON.parse silently keep a last value.
|
|
364
|
+
*/
|
|
365
|
+
export function parseIndexPublicationCoverageJson(raw) {
|
|
366
|
+
if (typeof raw !== "string")
|
|
367
|
+
return invalid("index publication coverage JSON must be a string");
|
|
368
|
+
let parsed;
|
|
369
|
+
try {
|
|
370
|
+
parsed = JSON.parse(raw);
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
return invalid("index publication coverage JSON is malformed");
|
|
374
|
+
}
|
|
375
|
+
const coverage = validateCoverageMap(parsed);
|
|
376
|
+
if (canonicalIndexPublicationCoverageJson(coverage) !== raw) {
|
|
377
|
+
return invalid("index publication coverage JSON is not canonical");
|
|
378
|
+
}
|
|
379
|
+
return coverage;
|
|
380
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
declare const PUBLICATION_POLICY_VERSION: 1;
|
|
2
|
+
export type AccountIndexPublicationScope = {
|
|
3
|
+
kind: "account";
|
|
4
|
+
vaultId: string;
|
|
5
|
+
};
|
|
6
|
+
export type TeamIndexPublicationScope = {
|
|
7
|
+
kind: "team";
|
|
8
|
+
spaceKey: string;
|
|
9
|
+
membershipVersion: number;
|
|
10
|
+
};
|
|
11
|
+
export type IndexPublicationScope = AccountIndexPublicationScope | TeamIndexPublicationScope;
|
|
12
|
+
/** The exact archive entry which must be remotely discoverable. */
|
|
13
|
+
export type IndexPublicationRequirement = {
|
|
14
|
+
archiveId: string;
|
|
15
|
+
archiveRef: string;
|
|
16
|
+
entryDigest: string;
|
|
17
|
+
scope: IndexPublicationScope;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* A positive receipt returned only after the registry/object read-after-write
|
|
21
|
+
* checks have succeeded. The receipt is useful only for its exact requirement.
|
|
22
|
+
*/
|
|
23
|
+
export type IndexPublicationReceipt = {
|
|
24
|
+
requirementKey: string;
|
|
25
|
+
policyVersion: typeof PUBLICATION_POLICY_VERSION;
|
|
26
|
+
publisherId: string;
|
|
27
|
+
generationId: string;
|
|
28
|
+
registryReceiptId: string;
|
|
29
|
+
registryRevision: number;
|
|
30
|
+
objectRef: string;
|
|
31
|
+
ciphertextSha256: string;
|
|
32
|
+
ciphertextBytes: number;
|
|
33
|
+
committedAt: string;
|
|
34
|
+
};
|
|
35
|
+
export type IndexPublicationReceiptDetails = Omit<IndexPublicationReceipt, "requirementKey" | "policyVersion"> & Partial<Pick<IndexPublicationReceipt, "requirementKey" | "policyVersion">>;
|
|
36
|
+
/** Optimistic token for one logical scope/archive obligation. */
|
|
37
|
+
export type IndexPublicationRequirementHandle = {
|
|
38
|
+
logicalKey: string;
|
|
39
|
+
requirementKey: string;
|
|
40
|
+
ledgerGeneration: string;
|
|
41
|
+
requirementRevision: number;
|
|
42
|
+
};
|
|
43
|
+
export type UnsatisfiedIndexPublicationRequirement = IndexPublicationRequirementHandle & {
|
|
44
|
+
requirement: IndexPublicationRequirement;
|
|
45
|
+
requiredAt: string;
|
|
46
|
+
};
|
|
47
|
+
export type IndexPublicationRequirementStatus = IndexPublicationRequirementHandle & {
|
|
48
|
+
requirement: IndexPublicationRequirement;
|
|
49
|
+
requiredAt: string;
|
|
50
|
+
receipt: IndexPublicationReceipt | null;
|
|
51
|
+
};
|
|
52
|
+
export type IndexPublicationKeyMaterial = string | {
|
|
53
|
+
phrase: string;
|
|
54
|
+
} | {
|
|
55
|
+
vaultId: string;
|
|
56
|
+
} | {
|
|
57
|
+
key: Uint8Array;
|
|
58
|
+
};
|
|
59
|
+
export type IndexPublicationStateSnapshot = {
|
|
60
|
+
status: "ok";
|
|
61
|
+
generation: string;
|
|
62
|
+
revision: number;
|
|
63
|
+
requirements: number;
|
|
64
|
+
satisfied: number;
|
|
65
|
+
};
|
|
66
|
+
export type IndexPublicationStateRead = IndexPublicationStateSnapshot | {
|
|
67
|
+
status: "missing";
|
|
68
|
+
} | {
|
|
69
|
+
status: "unreadable";
|
|
70
|
+
errorCode: string;
|
|
71
|
+
};
|
|
72
|
+
export type IndexPublicationReadOptions = {
|
|
73
|
+
/** Test seam for deterministic permission/I/O failure checks. */
|
|
74
|
+
readFile?: (path: string, encoding: "utf8") => Promise<string>;
|
|
75
|
+
};
|
|
76
|
+
export type IndexPublicationMutationOptions = IndexPublicationReadOptions & {
|
|
77
|
+
signal?: AbortSignal;
|
|
78
|
+
lockTimeoutMs?: number;
|
|
79
|
+
lockPollMs?: number;
|
|
80
|
+
now?: () => string;
|
|
81
|
+
uuid?: () => string;
|
|
82
|
+
};
|
|
83
|
+
export type RecordIndexPublicationRequirementOptions = IndexPublicationMutationOptions & {
|
|
84
|
+
/** `null` means create-only; a handle makes replacement a compare-and-set. */
|
|
85
|
+
expected?: IndexPublicationRequirementHandle | null;
|
|
86
|
+
};
|
|
87
|
+
export type RecordedIndexPublicationRequirement = {
|
|
88
|
+
requirement: IndexPublicationRequirement;
|
|
89
|
+
handle: IndexPublicationRequirementHandle;
|
|
90
|
+
};
|
|
91
|
+
export type IndexPublicationReceiptWrite = RecordedIndexPublicationRequirement & {
|
|
92
|
+
details: IndexPublicationReceiptDetails;
|
|
93
|
+
};
|
|
94
|
+
export declare class IndexPublicationStateError extends Error {
|
|
95
|
+
readonly code: "INDEX_PUBLICATION_STATE_CORRUPT" | "INDEX_PUBLICATION_STATE_UNREADABLE";
|
|
96
|
+
readonly cause?: unknown;
|
|
97
|
+
constructor(code: "INDEX_PUBLICATION_STATE_CORRUPT" | "INDEX_PUBLICATION_STATE_UNREADABLE", message: string, cause?: unknown);
|
|
98
|
+
}
|
|
99
|
+
export declare class IndexPublicationConflictError extends Error {
|
|
100
|
+
readonly code: "INDEX_PUBLICATION_STATE_CONFLICT";
|
|
101
|
+
constructor(message?: string);
|
|
102
|
+
}
|
|
103
|
+
export declare function indexPublicationStatePath(dataDir: string): string;
|
|
104
|
+
export declare function indexPublicationRequirementKey(material: IndexPublicationKeyMaterial, requirement: IndexPublicationRequirement): string;
|
|
105
|
+
export declare function indexPublicationLogicalKey(material: IndexPublicationKeyMaterial, requirement: IndexPublicationRequirement): string;
|
|
106
|
+
export declare function readIndexPublicationState(dataDir: string, options?: IndexPublicationReadOptions): Promise<IndexPublicationStateRead>;
|
|
107
|
+
/**
|
|
108
|
+
* Installs or refreshes one exact obligation. Repeating the same tuple is a
|
|
109
|
+
* true no-op; changing a tuple removes only that logical archive's receipt.
|
|
110
|
+
*/
|
|
111
|
+
export declare function recordIndexPublicationRequirement(dataDir: string, material: IndexPublicationKeyMaterial, requirement: IndexPublicationRequirement, options?: RecordIndexPublicationRequirementOptions): Promise<IndexPublicationRequirementHandle>;
|
|
112
|
+
/**
|
|
113
|
+
* Atomically installs a checkpoint's obligations with one lock acquisition and
|
|
114
|
+
* one fsync. Large vaults routinely contain thousands of sessions, so making
|
|
115
|
+
* each row its own durable transaction would turn indexing into minutes of
|
|
116
|
+
* avoidable disk latency. Duplicate logical slots are rejected because their
|
|
117
|
+
* order would otherwise decide which exact tuple survived.
|
|
118
|
+
*/
|
|
119
|
+
export declare function recordIndexPublicationRequirements(dataDir: string, material: IndexPublicationKeyMaterial, requirements: readonly IndexPublicationRequirement[], options?: IndexPublicationMutationOptions): Promise<RecordedIndexPublicationRequirement[]>;
|
|
120
|
+
/**
|
|
121
|
+
* Records positive publication proof only while the exact requirement and its
|
|
122
|
+
* local generation/revision still match the writer's pre-publication handle.
|
|
123
|
+
*/
|
|
124
|
+
export declare function recordIndexPublicationReceipt(dataDir: string, material: IndexPublicationKeyMaterial, requirement: IndexPublicationRequirement, expected: IndexPublicationRequirementHandle, details: IndexPublicationReceiptDetails, options?: IndexPublicationMutationOptions): Promise<boolean>;
|
|
125
|
+
/** Installs all still-current receipts from one verified remote generation. */
|
|
126
|
+
export declare function recordIndexPublicationReceipts(dataDir: string, material: IndexPublicationKeyMaterial, writes: readonly IndexPublicationReceiptWrite[], options?: IndexPublicationMutationOptions): Promise<{
|
|
127
|
+
recorded: number;
|
|
128
|
+
stale: number;
|
|
129
|
+
}>;
|
|
130
|
+
/** Missing, corrupt and unreadable state all deliberately return no proof. */
|
|
131
|
+
export declare function getIndexPublicationReceipt(dataDir: string, material: IndexPublicationKeyMaterial, requirement: IndexPublicationRequirement, options?: IndexPublicationReadOptions): Promise<IndexPublicationReceipt | null>;
|
|
132
|
+
export declare function hasIndexPublicationReceipt(dataDir: string, material: IndexPublicationKeyMaterial, requirement: IndexPublicationRequirement, options?: IndexPublicationReadOptions): Promise<boolean>;
|
|
133
|
+
/**
|
|
134
|
+
* Lists only durable obligations which lack their exact positive receipt.
|
|
135
|
+
* Missing state has no recorded requirements. Corrupt/EACCES state throws so
|
|
136
|
+
* callers cannot mistake an I/O fault for an authoritative empty list.
|
|
137
|
+
*/
|
|
138
|
+
export declare function listUnsatisfiedIndexPublicationRequirements(dataDir: string, material: IndexPublicationKeyMaterial, options?: IndexPublicationReadOptions): Promise<UnsatisfiedIndexPublicationRequirement[]>;
|
|
139
|
+
/**
|
|
140
|
+
* Lists every exact obligation together with its positive receipt, if any.
|
|
141
|
+
* Missing state is an empty non-authoritative list; corrupt or unreadable state
|
|
142
|
+
* throws so destructive callers cannot interpret an I/O failure as success.
|
|
143
|
+
*/
|
|
144
|
+
export declare function listIndexPublicationRequirements(dataDir: string, material: IndexPublicationKeyMaterial, options?: IndexPublicationReadOptions): Promise<IndexPublicationRequirementStatus[]>;
|
|
145
|
+
/** Invalidates one exact receipt; there is intentionally no global clear. */
|
|
146
|
+
export declare function invalidateIndexPublicationReceipt(dataDir: string, material: IndexPublicationKeyMaterial, requirement: IndexPublicationRequirement, expected: IndexPublicationRequirementHandle, options?: IndexPublicationMutationOptions): Promise<boolean>;
|
|
147
|
+
/** Removes one no-longer-applicable obligation using the same per-entry CAS. */
|
|
148
|
+
export declare function removeIndexPublicationRequirement(dataDir: string, material: IndexPublicationKeyMaterial, requirement: IndexPublicationRequirement, expected: IndexPublicationRequirementHandle, options?: IndexPublicationMutationOptions): Promise<boolean>;
|
|
149
|
+
export {};
|