@descryy/runtime-evidence-store 0.0.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/dist/evidence-store.d.ts +204 -0
- package/dist/evidence-store.d.ts.map +1 -0
- package/dist/evidence-store.js +511 -0
- package/dist/evidence-store.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/redaction.d.ts +120 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/redaction.js +337 -0
- package/dist/redaction.js.map +1 -0
- package/dist/replay.d.ts +118 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +104 -0
- package/dist/replay.js.map +1 -0
- package/package.json +29 -0
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evidence store (plan §13; plan §36 gate 9 "evidence persistence").
|
|
3
|
+
* Persistence keyed by executionId, backed by `node:sqlite` — mirrors
|
|
4
|
+
* descry-core's choice (CLAUDE.md: "node:sqlite behind a driver
|
|
5
|
+
* interface") for the same reason: zero native deps, no node-gyp-per-
|
|
6
|
+
* platform distribution problem. A full swappable-driver abstraction
|
|
7
|
+
* (descry-core's `SqlDriver`) is skipped here — one table, no real
|
|
8
|
+
* swap-target in view — but the row-rehydration pattern is kept:
|
|
9
|
+
* `node:sqlite` returns null-prototype row objects, which trips up
|
|
10
|
+
* `JSON.stringify` and structural-equality checks in ways that are easy to
|
|
11
|
+
* miss, so every row is spread into a plain object before use.
|
|
12
|
+
*
|
|
13
|
+
* `node:sqlite` is experimental in this Node version and prints an
|
|
14
|
+
* `ExperimentalWarning`. descry-core's own policy is to suppress that only
|
|
15
|
+
* at a CLI entrypoint, never in library code or tests — this package has
|
|
16
|
+
* no entrypoint of its own, so nothing here suppresses it.
|
|
17
|
+
*/
|
|
18
|
+
import { DatabaseSync } from "node:sqlite";
|
|
19
|
+
import { randomUUID } from "node:crypto";
|
|
20
|
+
import { EVIDENCE_SOURCES, REDACTION_STATUSES, RUNTIME_EVENT_TYPES, } from "@descryy/runtime-contracts";
|
|
21
|
+
import { redact } from "./redaction.js";
|
|
22
|
+
import { captureEnvironment, environmentDifferences, } from "./replay.js";
|
|
23
|
+
export class EvidenceStore {
|
|
24
|
+
#db;
|
|
25
|
+
#redactionPolicy;
|
|
26
|
+
#retentionMs;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.#redactionPolicy = options.redactionPolicy ?? {};
|
|
29
|
+
this.#retentionMs = options.retentionMs ?? null;
|
|
30
|
+
this.#db = new DatabaseSync(options.path);
|
|
31
|
+
this.#db.exec(`
|
|
32
|
+
CREATE TABLE IF NOT EXISTS evidence (
|
|
33
|
+
evidence_id TEXT PRIMARY KEY,
|
|
34
|
+
execution_id TEXT NOT NULL,
|
|
35
|
+
timestamp TEXT NOT NULL,
|
|
36
|
+
source TEXT NOT NULL,
|
|
37
|
+
service TEXT,
|
|
38
|
+
process TEXT,
|
|
39
|
+
event_type TEXT NOT NULL,
|
|
40
|
+
payload TEXT NOT NULL,
|
|
41
|
+
trace_id TEXT,
|
|
42
|
+
request_id TEXT,
|
|
43
|
+
correlation_id TEXT,
|
|
44
|
+
graph_node_id TEXT,
|
|
45
|
+
source_location TEXT,
|
|
46
|
+
stack_trace TEXT,
|
|
47
|
+
confidence REAL NOT NULL,
|
|
48
|
+
redaction_status TEXT NOT NULL,
|
|
49
|
+
collector_version TEXT NOT NULL
|
|
50
|
+
);
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_evidence_execution ON evidence(execution_id);
|
|
52
|
+
-- §24's missing half. Evidence survived a process restart; the
|
|
53
|
+
-- execution that produced it did not -- it lived in
|
|
54
|
+
-- ExecutionController's memory and nowhere on disk, so after a
|
|
55
|
+
-- restart the store held rows keyed by an executionId that no longer
|
|
56
|
+
-- resolved to an application, a commit, or a configuration.
|
|
57
|
+
--
|
|
58
|
+
-- A NEW TABLE, never a new column on the evidence table. An added
|
|
59
|
+
-- table appears under CREATE TABLE IF NOT EXISTS on an existing store
|
|
60
|
+
-- file;
|
|
61
|
+
-- an added column would need a migration this package has no
|
|
62
|
+
-- mechanism for, and inventing one to persist a fingerprint would be
|
|
63
|
+
-- a schema-versioning decision taken sideways.
|
|
64
|
+
CREATE TABLE IF NOT EXISTS executions (
|
|
65
|
+
execution_id TEXT PRIMARY KEY,
|
|
66
|
+
application TEXT NOT NULL,
|
|
67
|
+
repository TEXT NOT NULL,
|
|
68
|
+
commit_sha TEXT NOT NULL,
|
|
69
|
+
recorded_at TEXT NOT NULL,
|
|
70
|
+
configuration TEXT NOT NULL,
|
|
71
|
+
environment TEXT NOT NULL,
|
|
72
|
+
redacted_keys TEXT NOT NULL
|
|
73
|
+
);
|
|
74
|
+
CREATE INDEX IF NOT EXISTS idx_evidence_trace ON evidence(trace_id);
|
|
75
|
+
CREATE INDEX IF NOT EXISTS idx_evidence_request ON evidence(request_id);
|
|
76
|
+
CREATE INDEX IF NOT EXISTS idx_evidence_correlation ON evidence(correlation_id);
|
|
77
|
+
CREATE INDEX IF NOT EXISTS idx_evidence_service ON evidence(service);
|
|
78
|
+
CREATE INDEX IF NOT EXISTS idx_evidence_node ON evidence(graph_node_id);
|
|
79
|
+
CREATE INDEX IF NOT EXISTS idx_evidence_timestamp ON evidence(timestamp);
|
|
80
|
+
-- An expression index, because source_location is stored as JSON
|
|
81
|
+
-- rather than split into columns. Splitting it would be the faster
|
|
82
|
+
-- schema and would also mean migrating every existing store on disk;
|
|
83
|
+
-- the JSON stays authoritative and the index makes the file lookup a
|
|
84
|
+
-- seek instead of a scan.
|
|
85
|
+
CREATE INDEX IF NOT EXISTS idx_evidence_source_file ON evidence(json_extract(source_location, '$.file'));
|
|
86
|
+
`);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The only way to persist evidence. Redaction runs here, unconditionally
|
|
90
|
+
* — `EvidenceInput` has no `redactionStatus` field a caller could set,
|
|
91
|
+
* so there is no way to write a record claiming redaction happened
|
|
92
|
+
* without redact() actually having run. This is the structural
|
|
93
|
+
* guarantee plan §28 asks for: "raw runtime evidence must not
|
|
94
|
+
* automatically become unrestricted AI context."
|
|
95
|
+
*/
|
|
96
|
+
write(input) {
|
|
97
|
+
// **Validated at the boundary, not trusted from the type system (§3).**
|
|
98
|
+
// `EvidenceInput` is typed, and inside one compiled program that is
|
|
99
|
+
// enough. It stops being enough the moment a value arrives from anywhere
|
|
100
|
+
// the compiler did not check — a JSON body, a stored row from another
|
|
101
|
+
// version, a hand-written fixture, a collector built against an older
|
|
102
|
+
// contract. §3's own rows are "invalid event rejected" and "unknown event
|
|
103
|
+
// handled explicitly", and a compile-time union answers neither.
|
|
104
|
+
//
|
|
105
|
+
// Throws rather than coerces. A caller writing an event type this build
|
|
106
|
+
// does not know is a bug, and the cheapest moment to say so is before the
|
|
107
|
+
// row exists.
|
|
108
|
+
assertInVocabulary("eventType", input.eventType, RUNTIME_EVENT_TYPES);
|
|
109
|
+
assertInVocabulary("source", input.source, EVIDENCE_SOURCES);
|
|
110
|
+
const { payload, redactionStatus } = redact(input.payload, this.#redactionPolicy);
|
|
111
|
+
const evidence = { ...input, evidenceId: randomUUID(), payload, redactionStatus };
|
|
112
|
+
this.#db
|
|
113
|
+
.prepare(`INSERT INTO evidence (
|
|
114
|
+
evidence_id, execution_id, timestamp, source, service, process, event_type,
|
|
115
|
+
payload, trace_id, request_id, correlation_id, graph_node_id,
|
|
116
|
+
source_location, stack_trace, confidence, redaction_status, collector_version
|
|
117
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
118
|
+
.run(evidence.evidenceId, evidence.executionId, evidence.timestamp, evidence.source, evidence.service, evidence.process, evidence.eventType, JSON.stringify(evidence.payload), evidence.traceId, evidence.requestId, evidence.correlationId, evidence.graphNodeId, evidence.sourceLocation ? JSON.stringify(evidence.sourceLocation) : null, evidence.stackTrace ? JSON.stringify(evidence.stackTrace) : null, evidence.confidence, evidence.redactionStatus, evidence.collectorVersion);
|
|
119
|
+
return evidence;
|
|
120
|
+
}
|
|
121
|
+
/** Every item this execution produced, oldest first — retrievable after the execution has ended, as long as the store is reopened against the same file (plan §36 gate 9). */
|
|
122
|
+
getByExecution(executionId) {
|
|
123
|
+
const rows = this.#db
|
|
124
|
+
.prepare(`SELECT * FROM evidence WHERE execution_id = ? ORDER BY timestamp ASC`)
|
|
125
|
+
.all(executionId);
|
|
126
|
+
return rows.map(rowToEvidence);
|
|
127
|
+
}
|
|
128
|
+
getById(evidenceId) {
|
|
129
|
+
const row = this.#db.prepare(`SELECT * FROM evidence WHERE evidence_id = ?`).get(evidenceId);
|
|
130
|
+
return row ? rowToEvidence(row) : null;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Every row whose enum fields this build cannot interpret (§3).
|
|
134
|
+
*
|
|
135
|
+
* The explicit runtime path for an unknown event. Reads the raw columns
|
|
136
|
+
* without rehydrating, so it works on precisely the store that `query()`
|
|
137
|
+
* refuses — a caller that hits the refusal can call this to learn which
|
|
138
|
+
* rows, which fields and which values, instead of having a throw and no
|
|
139
|
+
* next step.
|
|
140
|
+
*
|
|
141
|
+
* Deliberately reports rather than repairs. Rewriting an unknown
|
|
142
|
+
* `event_type` to something this build knows would be inventing an
|
|
143
|
+
* observation, and deleting the row would destroy evidence that a newer
|
|
144
|
+
* build can read perfectly well.
|
|
145
|
+
*/
|
|
146
|
+
integrityCheck() {
|
|
147
|
+
const rows = this.#db
|
|
148
|
+
.prepare(`SELECT evidence_id, source, event_type, redaction_status FROM evidence`)
|
|
149
|
+
.all();
|
|
150
|
+
const issues = [];
|
|
151
|
+
for (const raw of rows) {
|
|
152
|
+
const row = { ...raw };
|
|
153
|
+
for (const [field, value, vocabulary] of [
|
|
154
|
+
["eventType", row.event_type, RUNTIME_EVENT_TYPES],
|
|
155
|
+
["source", row.source, EVIDENCE_SOURCES],
|
|
156
|
+
["redactionStatus", row.redaction_status, REDACTION_STATUSES],
|
|
157
|
+
]) {
|
|
158
|
+
if (!vocabulary.includes(value)) {
|
|
159
|
+
issues.push({ evidenceId: row.evidence_id, field, value });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return issues;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Persist the execution behind an `executionId` (§24).
|
|
167
|
+
*
|
|
168
|
+
* **The configuration is redacted on the way in, and that is not an
|
|
169
|
+
* over-application of §20.** Every one of §20's rows is about an evidence
|
|
170
|
+
* payload, but `configuration.services[x].command` is a command line, and
|
|
171
|
+
* a command line is one of the classic places a credential travels:
|
|
172
|
+
* `node server.mjs --api-key=…`. The same `redact()` the evidence write
|
|
173
|
+
* path uses runs here, on the same terms, and `redactedKeys` reports what
|
|
174
|
+
* it caught rather than leaving a caller to guess.
|
|
175
|
+
*
|
|
176
|
+
* Re-recording the same `executionId` replaces the row. An execution is
|
|
177
|
+
* recorded once by the component that owns it; a second record for the
|
|
178
|
+
* same id is a re-run of that recording, not a second execution.
|
|
179
|
+
*/
|
|
180
|
+
recordExecution(input) {
|
|
181
|
+
const { payload: configuration, redactedKeys } = redact(input.configuration, this.#redactionPolicy);
|
|
182
|
+
const environment = input.environment ?? captureEnvironment(serviceCommandsOf(configuration));
|
|
183
|
+
const recordedAt = input.recordedAt ?? new Date().toISOString();
|
|
184
|
+
this.#db
|
|
185
|
+
.prepare(`INSERT INTO executions (
|
|
186
|
+
execution_id, application, repository, commit_sha, recorded_at,
|
|
187
|
+
configuration, environment, redacted_keys
|
|
188
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
189
|
+
ON CONFLICT(execution_id) DO UPDATE SET
|
|
190
|
+
application = excluded.application,
|
|
191
|
+
repository = excluded.repository,
|
|
192
|
+
commit_sha = excluded.commit_sha,
|
|
193
|
+
recorded_at = excluded.recorded_at,
|
|
194
|
+
configuration = excluded.configuration,
|
|
195
|
+
environment = excluded.environment,
|
|
196
|
+
redacted_keys = excluded.redacted_keys`)
|
|
197
|
+
.run(input.executionId, input.application, input.repository, input.commit, recordedAt, JSON.stringify(configuration), JSON.stringify(environment), JSON.stringify(redactedKeys));
|
|
198
|
+
return {
|
|
199
|
+
executionId: input.executionId,
|
|
200
|
+
application: input.application,
|
|
201
|
+
repository: input.repository,
|
|
202
|
+
commit: input.commit,
|
|
203
|
+
recordedAt,
|
|
204
|
+
configuration,
|
|
205
|
+
environment,
|
|
206
|
+
redactedKeys,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
getExecution(executionId) {
|
|
210
|
+
const row = this.#db.prepare(`SELECT * FROM executions WHERE execution_id = ?`).get(executionId);
|
|
211
|
+
if (row === undefined)
|
|
212
|
+
return null;
|
|
213
|
+
return {
|
|
214
|
+
executionId: String(row["execution_id"]),
|
|
215
|
+
application: String(row["application"]),
|
|
216
|
+
repository: String(row["repository"]),
|
|
217
|
+
commit: String(row["commit_sha"]),
|
|
218
|
+
recordedAt: String(row["recorded_at"]),
|
|
219
|
+
configuration: JSON.parse(String(row["configuration"])),
|
|
220
|
+
environment: JSON.parse(String(row["environment"])),
|
|
221
|
+
redactedKeys: JSON.parse(String(row["redacted_keys"])),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Replay an execution's evidence (§24).
|
|
226
|
+
*
|
|
227
|
+
* **Replays evidence, not the application.** Nothing is spawned, driven or
|
|
228
|
+
* reissued; this returns what was recorded, in recorded order, so
|
|
229
|
+
* correlation, findings and an evidence package can be re-derived without
|
|
230
|
+
* booting anything. A replay that quietly re-executed would produce a *new*
|
|
231
|
+
* observation while claiming to reproduce an old one.
|
|
232
|
+
*
|
|
233
|
+
* Throws for an unrecorded execution rather than returning an empty
|
|
234
|
+
* replay. Evidence rows can exist for an `executionId` that was never
|
|
235
|
+
* recorded — that is precisely the pre-§24 state — and answering with
|
|
236
|
+
* "here is your replay, minus the execution" would present the gap as a
|
|
237
|
+
* result.
|
|
238
|
+
*/
|
|
239
|
+
replay(executionId, currentEnvironment) {
|
|
240
|
+
const execution = this.getExecution(executionId);
|
|
241
|
+
if (execution === null) {
|
|
242
|
+
throw new Error(`replay: no execution recorded for ${JSON.stringify(executionId)}. Evidence may still exist for ` +
|
|
243
|
+
"it — this store held evidence for unrecorded executions before recordExecution existed — but " +
|
|
244
|
+
"a replay without the configuration and environment it ran under is not a replay.");
|
|
245
|
+
}
|
|
246
|
+
const evidence = this.getByExecution(executionId);
|
|
247
|
+
const current = currentEnvironment ?? captureEnvironment(serviceCommandsOf(execution.configuration));
|
|
248
|
+
const warnings = [];
|
|
249
|
+
const differences = environmentDifferences(execution.environment, current);
|
|
250
|
+
if (differences.length > 0) {
|
|
251
|
+
warnings.push({
|
|
252
|
+
kind: "environment-changed",
|
|
253
|
+
detail: `The environment differs from the one this execution was recorded in: ${differences.join("; ")}. ` +
|
|
254
|
+
"Reported, not enforced — whether it matters is a judgement this store has no information to make.",
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
if (evidence.length === 0) {
|
|
258
|
+
warnings.push({
|
|
259
|
+
kind: "no-evidence",
|
|
260
|
+
detail: "The execution is recorded but produced no stored evidence. That is a real state (a run that " +
|
|
261
|
+
"observed nothing) and is not the same as an unrecorded execution, which throws.",
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
// getByExecution orders by timestamp, so this cannot detect a bad sort —
|
|
265
|
+
// it detects a recorded stream whose own timestamps were never
|
|
266
|
+
// monotonic, which means "recorded order" and "the order things
|
|
267
|
+
// happened" are not the same list and a reader replaying by index is
|
|
268
|
+
// reading a sequence that never occurred.
|
|
269
|
+
for (let i = 1; i < evidence.length; i += 1) {
|
|
270
|
+
if (evidence[i - 1].timestamp === evidence[i].timestamp)
|
|
271
|
+
continue;
|
|
272
|
+
if (evidence[i - 1].timestamp < evidence[i].timestamp)
|
|
273
|
+
continue;
|
|
274
|
+
warnings.push({
|
|
275
|
+
kind: "unordered-timestamps",
|
|
276
|
+
detail: "At least one pair of evidence items is out of timestamp order after sorting, which means the " +
|
|
277
|
+
"recorded timestamps are not a total order. Replay order is by timestamp, so the sequence " +
|
|
278
|
+
"returned here is not necessarily the sequence that occurred.",
|
|
279
|
+
});
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
return { execution, evidence, currentEnvironment: current, warnings };
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Record that correlation resolved this evidence to a graph node.
|
|
286
|
+
*
|
|
287
|
+
* **Written because the correlation was never durable, and that made two
|
|
288
|
+
* of the evidence package's four selection tiers inert.** Found by
|
|
289
|
+
* mutation, not by reading: `assembleEvidencePackage`'s tier 2 queries
|
|
290
|
+
* `graphNodeId`, and a mutation that dropped the execution scope from that
|
|
291
|
+
* query changed nothing at all — because in the real pipeline no stored row
|
|
292
|
+
* has a `graphNodeId` to match. `graphNodeId` was set in memory at
|
|
293
|
+
* composition time, on a copy, and thrown away with the array that held it.
|
|
294
|
+
* Every downstream question of the form "what else named this node" was
|
|
295
|
+
* therefore answerable only for evidence some caller happened to still be
|
|
296
|
+
* holding.
|
|
297
|
+
*
|
|
298
|
+
* **Refuses to re-attribute to a different node.** One observation
|
|
299
|
+
* genuinely can name two graph entities (RT-052's backend exception names
|
|
300
|
+
* the endpoint in its text and the function in its stack), and the answer
|
|
301
|
+
* to that is `EvidenceAttribution` passed alongside the evidence — an
|
|
302
|
+
* output of correlation, not a property of the observation. Letting this
|
|
303
|
+
* method overwrite would silently discard the first correlation and leave
|
|
304
|
+
* the store asserting the second as if it were the only one. Idempotent
|
|
305
|
+
* for the same node, so re-running a correlation pass is safe.
|
|
306
|
+
*
|
|
307
|
+
* Throws for an unknown `evidenceId`: a caller attributing evidence this
|
|
308
|
+
* store does not hold has a bug, and writing nothing would hide it.
|
|
309
|
+
*/
|
|
310
|
+
attribute(evidenceId, graphNodeId) {
|
|
311
|
+
const existing = this.getById(evidenceId);
|
|
312
|
+
if (existing === null) {
|
|
313
|
+
throw new Error(`attribute: no evidence with id ${JSON.stringify(evidenceId)} in this store`);
|
|
314
|
+
}
|
|
315
|
+
if (existing.graphNodeId !== null && existing.graphNodeId !== graphNodeId) {
|
|
316
|
+
throw new Error(`attribute: evidence ${evidenceId} is already attributed to ${existing.graphNodeId}; refusing to ` +
|
|
317
|
+
`overwrite with ${graphNodeId}. One observation naming two nodes is expressed as an ` +
|
|
318
|
+
"EvidenceAttribution alongside the evidence, not by rewriting the stored one.");
|
|
319
|
+
}
|
|
320
|
+
if (existing.graphNodeId === graphNodeId)
|
|
321
|
+
return;
|
|
322
|
+
this.#db.prepare(`UPDATE evidence SET graph_node_id = ? WHERE evidence_id = ?`).run(graphNodeId, evidenceId);
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* The general query surface (§19).
|
|
326
|
+
*
|
|
327
|
+
* Before this, the store answered two questions — by id and by execution —
|
|
328
|
+
* and §19 asks for six axes: trace, service, graph node, source location,
|
|
329
|
+
* time, and execution. **Five of the six did not exist**, which is
|
|
330
|
+
* invisible from "evidence persistence works" and was found by auditing
|
|
331
|
+
* the checklist rather than by anything failing.
|
|
332
|
+
*
|
|
333
|
+
* **`executionId` is required and may be explicitly `null`, and that is
|
|
334
|
+
* the load-bearing design choice here.** Every other field is optional.
|
|
335
|
+
* Evidence not leaking between executions is a guarantee this repo
|
|
336
|
+
* enforces elsewhere (`composeFinding` excludes foreign executions with a
|
|
337
|
+
* named reason), and a query is the obvious way to lose it: ask for a
|
|
338
|
+
* trace id and quietly receive a prior run's evidence too. Making the
|
|
339
|
+
* field optional-with-a-default would mean the safe behaviour depends on
|
|
340
|
+
* remembering to pass something, which is how the default gets forgotten
|
|
341
|
+
* exactly once. Passing `null` is a deliberate statement that a
|
|
342
|
+
* cross-execution answer is wanted — the same discipline
|
|
343
|
+
* `resolveSymbolNode` applies to `repo`, where there is no honest default
|
|
344
|
+
* either.
|
|
345
|
+
*
|
|
346
|
+
* Criteria combine with AND. An empty criteria object plus a null
|
|
347
|
+
* execution returns everything, which is a real request (a store dump)
|
|
348
|
+
* and is spelled out rather than reachable by accident.
|
|
349
|
+
*/
|
|
350
|
+
query(criteria) {
|
|
351
|
+
const clauses = [];
|
|
352
|
+
const params = [];
|
|
353
|
+
if (criteria.executionId !== null) {
|
|
354
|
+
clauses.push("execution_id = ?");
|
|
355
|
+
params.push(criteria.executionId);
|
|
356
|
+
}
|
|
357
|
+
// Each of these is an exact match, never a LIKE. A prefix or substring
|
|
358
|
+
// match on a trace id or a service name would return evidence about a
|
|
359
|
+
// *different* thing that happens to share a prefix -- RT-031's
|
|
360
|
+
// identity-proxy class, and the last place it belongs is the query
|
|
361
|
+
// layer that everything above reads through.
|
|
362
|
+
for (const [column, value] of [
|
|
363
|
+
["trace_id", criteria.traceId],
|
|
364
|
+
["request_id", criteria.requestId],
|
|
365
|
+
["correlation_id", criteria.correlationId],
|
|
366
|
+
["service", criteria.service],
|
|
367
|
+
["source", criteria.source],
|
|
368
|
+
["event_type", criteria.eventType],
|
|
369
|
+
["graph_node_id", criteria.graphNodeId],
|
|
370
|
+
]) {
|
|
371
|
+
if (value === undefined)
|
|
372
|
+
continue;
|
|
373
|
+
clauses.push(`${column} = ?`);
|
|
374
|
+
params.push(value);
|
|
375
|
+
}
|
|
376
|
+
if (criteria.sourceFile !== undefined) {
|
|
377
|
+
clauses.push("json_extract(source_location, '$.file') = ?");
|
|
378
|
+
params.push(criteria.sourceFile);
|
|
379
|
+
}
|
|
380
|
+
if (criteria.sourceLine !== undefined) {
|
|
381
|
+
clauses.push("json_extract(source_location, '$.line') = ?");
|
|
382
|
+
params.push(criteria.sourceLine);
|
|
383
|
+
}
|
|
384
|
+
// ISO-8601 UTC strings sort lexicographically in the same order they
|
|
385
|
+
// sort chronologically, which is why `timestamp` is comparable as TEXT.
|
|
386
|
+
// Inclusive at both ends: a caller asking for a window around a known
|
|
387
|
+
// event should get that event.
|
|
388
|
+
if (criteria.from !== undefined) {
|
|
389
|
+
clauses.push("timestamp >= ?");
|
|
390
|
+
params.push(criteria.from);
|
|
391
|
+
}
|
|
392
|
+
if (criteria.to !== undefined) {
|
|
393
|
+
clauses.push("timestamp <= ?");
|
|
394
|
+
params.push(criteria.to);
|
|
395
|
+
}
|
|
396
|
+
const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
397
|
+
const limit = criteria.limit !== undefined ? "LIMIT ?" : "";
|
|
398
|
+
if (criteria.limit !== undefined)
|
|
399
|
+
params.push(criteria.limit);
|
|
400
|
+
const rows = this.#db
|
|
401
|
+
.prepare(`SELECT * FROM evidence ${where} ORDER BY timestamp ASC ${limit}`)
|
|
402
|
+
.all(...params);
|
|
403
|
+
return rows.map(rowToEvidence);
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Deletes every row whose own `timestamp` is older than `retentionMs`
|
|
407
|
+
* (constructor option) relative to `now` (defaults to the real current
|
|
408
|
+
* time; overridable so a test can purge without a real sleep). Never
|
|
409
|
+
* called automatically -- no timer, no call from `write()` -- a caller
|
|
410
|
+
* decides when retention actually runs. Throws rather than silently
|
|
411
|
+
* no-op-ing if `retentionMs` was never configured: a caller explicitly
|
|
412
|
+
* asking to purge with no policy in place is a configuration mistake
|
|
413
|
+
* worth surfacing, not masking.
|
|
414
|
+
*/
|
|
415
|
+
purgeExpired(now = new Date()) {
|
|
416
|
+
if (this.#retentionMs === null) {
|
|
417
|
+
throw new Error("purgeExpired() called but no retentionMs was configured on this EvidenceStore.");
|
|
418
|
+
}
|
|
419
|
+
const cutoff = new Date(now.getTime() - this.#retentionMs).toISOString();
|
|
420
|
+
const result = this.#db.prepare(`DELETE FROM evidence WHERE timestamp < ?`).run(cutoff);
|
|
421
|
+
return Number(result.changes);
|
|
422
|
+
}
|
|
423
|
+
close() {
|
|
424
|
+
this.#db.close();
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Throws when a value is outside the vocabulary this build knows.
|
|
429
|
+
*
|
|
430
|
+
* The message names the value **and** the vocabulary, because the two useful
|
|
431
|
+
* conclusions are opposite: an unknown value is either a typo (fix the caller)
|
|
432
|
+
* or a newer contract (this build is the old one), and only seeing both lists
|
|
433
|
+
* distinguishes them.
|
|
434
|
+
*/
|
|
435
|
+
function assertInVocabulary(field, value, vocabulary) {
|
|
436
|
+
if (vocabulary.includes(value))
|
|
437
|
+
return;
|
|
438
|
+
throw new Error(`EvidenceStore: ${field} ${JSON.stringify(value)} is not one of ${JSON.stringify(vocabulary)}. ` +
|
|
439
|
+
"Either the caller is wrong, or this store is being written by a build with a newer contract " +
|
|
440
|
+
"than the one reading it — the two need opposite fixes, so neither is assumed.");
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Rehydrate a row, refusing one this build cannot interpret.
|
|
444
|
+
*
|
|
445
|
+
* **The previous version blind-cast every enum field** — `row.event_type as
|
|
446
|
+
* Evidence["eventType"]` — which is a `as` that cannot fail and a value that
|
|
447
|
+
* can. A store file written by a build with an extra `RuntimeEventType`, a
|
|
448
|
+
* restored backup, or a hand-edited row produced a fully-typed `Evidence`
|
|
449
|
+
* whose `eventType` no `switch` in this program handles, and nothing anywhere
|
|
450
|
+
* said so. §3's "unknown event handled explicitly" row was open for exactly
|
|
451
|
+
* this, and the handling could not be added upstream because there was no
|
|
452
|
+
* point at which the unknown was ever recognised as unknown.
|
|
453
|
+
*
|
|
454
|
+
* **It throws rather than skipping.** Skipping would return fewer rows while
|
|
455
|
+
* every caller treated the result as complete — the "unsupported silently
|
|
456
|
+
* becomes no failure" shape, applied to a query result, which is the worst
|
|
457
|
+
* place for it: `composeFinding` would compose over a subset and report
|
|
458
|
+
* confidently. `integrityCheck()` exists so a caller facing this can find out
|
|
459
|
+
* how bad it is without the throw being a dead end.
|
|
460
|
+
*/
|
|
461
|
+
function rowToEvidence(raw) {
|
|
462
|
+
const row = { ...raw };
|
|
463
|
+
assertInVocabulary("eventType", row.event_type, RUNTIME_EVENT_TYPES);
|
|
464
|
+
assertInVocabulary("source", row.source, EVIDENCE_SOURCES);
|
|
465
|
+
assertInVocabulary("redactionStatus", row.redaction_status, REDACTION_STATUSES);
|
|
466
|
+
return {
|
|
467
|
+
evidenceId: row.evidence_id,
|
|
468
|
+
executionId: row.execution_id,
|
|
469
|
+
timestamp: row.timestamp,
|
|
470
|
+
source: row.source,
|
|
471
|
+
service: row.service,
|
|
472
|
+
process: row.process,
|
|
473
|
+
eventType: row.event_type,
|
|
474
|
+
payload: JSON.parse(row.payload),
|
|
475
|
+
traceId: row.trace_id,
|
|
476
|
+
requestId: row.request_id,
|
|
477
|
+
correlationId: row.correlation_id,
|
|
478
|
+
graphNodeId: row.graph_node_id,
|
|
479
|
+
sourceLocation: row.source_location
|
|
480
|
+
? JSON.parse(row.source_location)
|
|
481
|
+
: null,
|
|
482
|
+
stackTrace: row.stack_trace ? JSON.parse(row.stack_trace) : null,
|
|
483
|
+
confidence: row.confidence,
|
|
484
|
+
redactionStatus: row.redaction_status,
|
|
485
|
+
collectorVersion: row.collector_version,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* `serviceName -> command` out of an `ExecutionConfiguration`-shaped object.
|
|
490
|
+
*
|
|
491
|
+
* Written defensively against `unknown` rather than typed against
|
|
492
|
+
* `ExecutionConfiguration`, because the configuration reaching this store has
|
|
493
|
+
* already been through `redact()` and is no longer that type: a redacted
|
|
494
|
+
* command is the string `[REDACTED]`, which is still a string, but a redacted
|
|
495
|
+
* *services* object could in principle be a marker too. Reading it
|
|
496
|
+
* structurally means a surprising shape yields an empty map rather than a
|
|
497
|
+
* throw inside a persistence call.
|
|
498
|
+
*/
|
|
499
|
+
function serviceCommandsOf(configuration) {
|
|
500
|
+
const services = configuration?.services;
|
|
501
|
+
if (services === null || typeof services !== "object")
|
|
502
|
+
return {};
|
|
503
|
+
const commands = {};
|
|
504
|
+
for (const [name, service] of Object.entries(services)) {
|
|
505
|
+
const command = service?.command;
|
|
506
|
+
if (typeof command === "string")
|
|
507
|
+
commands[name] = command;
|
|
508
|
+
}
|
|
509
|
+
return commands;
|
|
510
|
+
}
|
|
511
|
+
//# sourceMappingURL=evidence-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence-store.js","sourceRoot":"","sources":["../src/evidence-store.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { RedactionResult, RedactionPolicy } from "./redaction.ts";
|
|
2
|
+
export { redact, collidingKeys, redactCommand } from "./redaction.ts";
|
|
3
|
+
export type { EvidenceStoreOptions, EvidenceInput, EvidenceQuery } from "./evidence-store.ts";
|
|
4
|
+
export { EvidenceStore } from "./evidence-store.ts";
|
|
5
|
+
export { captureEnvironment, environmentDifferences, type EnvironmentFingerprint, type RecordedExecution, type Replay, type ReplayWarning, } from "./replay.ts";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEtE,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,MAAM,EACX,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGtE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GAKvB,MAAM,aAAa,CAAC"}
|