@descryy/runtime-evidence-store 0.2.0 → 0.4.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/LICENSE +6 -0
- package/dist/evidence-store.d.ts +78 -118
- package/dist/evidence-store.d.ts.map +1 -1
- package/dist/evidence-store.js +106 -174
- package/dist/evidence-store.js.map +1 -1
- package/dist/redaction.d.ts +34 -89
- package/dist/redaction.d.ts.map +1 -1
- package/dist/redaction.js +63 -156
- package/dist/redaction.js.map +1 -1
- package/dist/replay.d.ts +26 -71
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +24 -62
- package/dist/replay.js.map +1 -1
- package/package.json +7 -2
package/LICENSE
ADDED
package/dist/evidence-store.d.ts
CHANGED
|
@@ -1,44 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Evidence store
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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.
|
|
2
|
+
* Evidence store, keyed by executionId, backed by `node:sqlite` (mirrors
|
|
3
|
+
* descry-core's driver choice: zero native deps, no node-gyp problem). No
|
|
4
|
+
* swappable-driver abstraction here — one table, no swap target — but the
|
|
5
|
+
* row-rehydration pattern is kept: `node:sqlite` returns null-prototype
|
|
6
|
+
* rows, which trips up `JSON.stringify` and equality checks, so every row
|
|
7
|
+
* is spread into a plain object before use.
|
|
12
8
|
*
|
|
13
|
-
* `node:sqlite`
|
|
14
|
-
*
|
|
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.
|
|
9
|
+
* `node:sqlite` prints an `ExperimentalWarning`; suppressed only at a CLI
|
|
10
|
+
* entrypoint by policy, and this package has none, so nothing suppresses it here.
|
|
17
11
|
*/
|
|
18
12
|
import { type Evidence, type EvidenceSource, type RuntimeEventType } from "@descryy/runtime-contracts";
|
|
19
13
|
import { type RedactionPolicy } from "./redaction.ts";
|
|
20
14
|
import { type EnvironmentFingerprint, type RecordedExecution, type Replay } from "./replay.ts";
|
|
21
15
|
export interface EvidenceStoreOptions {
|
|
22
|
-
/** A file path, or ":memory:" for tests. Persistence across
|
|
16
|
+
/** A file path, or ":memory:" for tests. Persistence across restarts requires a real path. */
|
|
23
17
|
readonly path: string;
|
|
24
|
-
/** Omit for the restricted default
|
|
18
|
+
/** Omit for the restricted default — a caller opts into anything looser, never the reverse. */
|
|
25
19
|
readonly redactionPolicy?: RedactionPolicy;
|
|
26
20
|
/**
|
|
27
|
-
* How long evidence survives a purgeExpired() call, in
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* explicitly by a caller, never on a timer this store owns itself.
|
|
31
|
-
* Orthogonal to replay (plan §30, not built here): retention controls
|
|
32
|
-
* how long evidence is kept, not what a replay does with what remains.
|
|
21
|
+
* How long evidence survives a `purgeExpired()` call, in ms since its
|
|
22
|
+
* `timestamp`. Null/omitted: never purged automatically — purge only
|
|
23
|
+
* runs when a caller explicitly calls it, never on a timer this store owns.
|
|
33
24
|
*/
|
|
34
25
|
readonly retentionMs?: number;
|
|
35
26
|
}
|
|
36
27
|
export type EvidenceInput = Omit<Evidence, "evidenceId" | "redactionStatus">;
|
|
37
|
-
/**
|
|
38
|
-
* Criteria for `EvidenceStore.query`. Every field combines with AND, and
|
|
39
|
-
* every match is exact — see `query`'s own header for why `executionId` is
|
|
40
|
-
* required-but-nullable rather than optional.
|
|
41
|
-
*/
|
|
28
|
+
/** Criteria for `EvidenceStore.query`. Every field combines with AND, every match exact — see `query`'s header for why `executionId` is required-but-nullable. */
|
|
42
29
|
export interface EvidenceQuery {
|
|
43
30
|
/** Required. `null` is a deliberate statement that a cross-execution answer is wanted; there is no honest default. */
|
|
44
31
|
readonly executionId: string | null;
|
|
@@ -63,30 +50,22 @@ export declare class EvidenceStore {
|
|
|
63
50
|
#private;
|
|
64
51
|
constructor(options: EvidenceStoreOptions);
|
|
65
52
|
/**
|
|
66
|
-
* The only way to persist evidence. Redaction runs here
|
|
53
|
+
* The only way to persist evidence. Redaction runs here unconditionally
|
|
67
54
|
* — `EvidenceInput` has no `redactionStatus` field a caller could set,
|
|
68
|
-
* so there
|
|
69
|
-
* without
|
|
70
|
-
* guarantee plan §28 asks for: "raw runtime evidence must not
|
|
71
|
-
* automatically become unrestricted AI context."
|
|
55
|
+
* so there's no way to write a record claiming redaction happened
|
|
56
|
+
* without it actually running (§28's structural guarantee).
|
|
72
57
|
*/
|
|
73
58
|
write(input: EvidenceInput): Evidence;
|
|
74
|
-
/** Every item this execution produced, oldest first — retrievable after the execution
|
|
59
|
+
/** Every item this execution produced, oldest first — retrievable after the execution ends, as long as the store reopens against the same file. */
|
|
75
60
|
getByExecution(executionId: string): Evidence[];
|
|
76
61
|
getById(evidenceId: string): Evidence | null;
|
|
77
62
|
/**
|
|
78
|
-
* Every row whose enum fields this build
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* next step.
|
|
85
|
-
*
|
|
86
|
-
* Deliberately reports rather than repairs. Rewriting an unknown
|
|
87
|
-
* `event_type` to something this build knows would be inventing an
|
|
88
|
-
* observation, and deleting the row would destroy evidence that a newer
|
|
89
|
-
* build can read perfectly well.
|
|
63
|
+
* Every row whose enum fields this build can't interpret. Reads raw
|
|
64
|
+
* columns without rehydrating, so it works on the store that `query()`
|
|
65
|
+
* refuses — gives a caller a next step instead of just a throw. Reports
|
|
66
|
+
* rather than repairs: rewriting an unknown value would invent an
|
|
67
|
+
* observation, and deleting the row would destroy evidence a newer build
|
|
68
|
+
* can read fine.
|
|
90
69
|
*/
|
|
91
70
|
integrityCheck(): readonly {
|
|
92
71
|
readonly evidenceId: string;
|
|
@@ -94,19 +73,15 @@ export declare class EvidenceStore {
|
|
|
94
73
|
readonly value: string;
|
|
95
74
|
}[];
|
|
96
75
|
/**
|
|
97
|
-
* Persist the execution behind an `executionId
|
|
76
|
+
* Persist the execution behind an `executionId`.
|
|
98
77
|
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* `node server.mjs --api-key=…`. The same `redact()` the evidence write
|
|
104
|
-
* path uses runs here, on the same terms, and `redactedKeys` reports what
|
|
105
|
-
* it caught rather than leaving a caller to guess.
|
|
78
|
+
* The configuration is redacted on the way in too —
|
|
79
|
+
* `configuration.services[x].command` is a command line, a classic place
|
|
80
|
+
* a credential travels. Same `redact()` as the evidence write path;
|
|
81
|
+
* `redactedKeys` reports what it caught.
|
|
106
82
|
*
|
|
107
|
-
* Re-recording the same `executionId` replaces the row
|
|
108
|
-
*
|
|
109
|
-
* same id is a re-run of that recording, not a second execution.
|
|
83
|
+
* Re-recording the same `executionId` replaces the row — a second record
|
|
84
|
+
* is a re-run of that recording, not a second execution.
|
|
110
85
|
*/
|
|
111
86
|
recordExecution(input: {
|
|
112
87
|
readonly executionId: string;
|
|
@@ -117,86 +92,71 @@ export declare class EvidenceStore {
|
|
|
117
92
|
readonly recordedAt?: string;
|
|
118
93
|
readonly environment?: EnvironmentFingerprint;
|
|
119
94
|
}): RecordedExecution;
|
|
120
|
-
getExecution(executionId: string): RecordedExecution | null;
|
|
121
95
|
/**
|
|
122
|
-
*
|
|
96
|
+
* Every distinct `execution_id` in `evidence` with no matching row in
|
|
97
|
+
* `executions` — B7's enforcement half.
|
|
123
98
|
*
|
|
124
|
-
* **
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
99
|
+
* **Deliberately a query a caller runs, not a `FOREIGN KEY` constraint.**
|
|
100
|
+
* This store tolerates legacy orphans on purpose — `replay()`'s own error
|
|
101
|
+
* message names that exact state as the one every store predating
|
|
102
|
+
* `recordExecution` is in, and this package's own tests write evidence
|
|
103
|
+
* against an unrecorded execution as a real, intentional case. A hard
|
|
104
|
+
* constraint would refuse those writes (and every other test in this
|
|
105
|
+
* repo's two packages that constructs a store and calls `write()` without
|
|
106
|
+
* first calling `recordExecution`, which is most of them) rather than
|
|
107
|
+
* merely reporting the gap. Call this from a fresh execution's own tests
|
|
108
|
+
* — never against a pre-existing on-disk store you did not build in the
|
|
109
|
+
* test — to catch a NEW orphan the moment one is introduced, without
|
|
110
|
+
* breaking the many legacy and standalone callers this store still has.
|
|
111
|
+
*/
|
|
112
|
+
orphanedExecutionIds(): readonly string[];
|
|
113
|
+
getExecution(executionId: string): RecordedExecution | null;
|
|
114
|
+
/**
|
|
115
|
+
* Replay an execution's evidence. Replays evidence, not the application —
|
|
116
|
+
* nothing is spawned, driven or reissued, just returned in recorded
|
|
117
|
+
* order. A replay that re-executed would produce a *new* observation
|
|
118
|
+
* while claiming to reproduce an old one.
|
|
129
119
|
*
|
|
130
120
|
* Throws for an unrecorded execution rather than returning an empty
|
|
131
|
-
* replay
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* result.
|
|
121
|
+
* replay — evidence rows can exist for an id that was never recorded,
|
|
122
|
+
* and answering with "here is your replay, minus the execution" would
|
|
123
|
+
* present the gap as a result.
|
|
135
124
|
*/
|
|
136
125
|
replay(executionId: string, currentEnvironment?: EnvironmentFingerprint): Replay;
|
|
137
126
|
/**
|
|
138
127
|
* Record that correlation resolved this evidence to a graph node.
|
|
139
128
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* query changed nothing at all — because in the real pipeline no stored row
|
|
145
|
-
* has a `graphNodeId` to match. `graphNodeId` was set in memory at
|
|
146
|
-
* composition time, on a copy, and thrown away with the array that held it.
|
|
147
|
-
* Every downstream question of the form "what else named this node" was
|
|
148
|
-
* therefore answerable only for evidence some caller happened to still be
|
|
149
|
-
* holding.
|
|
129
|
+
* Written because correlation was never durable — `graphNodeId` was set
|
|
130
|
+
* in memory at composition time, on a copy, and thrown away with it,
|
|
131
|
+
* making two of the evidence package's four selection tiers inert.
|
|
132
|
+
* Found by mutation testing, not by reading.
|
|
150
133
|
*
|
|
151
|
-
*
|
|
152
|
-
* genuinely
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* method overwrite would silently discard the first correlation and leave
|
|
157
|
-
* the store asserting the second as if it were the only one. Idempotent
|
|
158
|
-
* for the same node, so re-running a correlation pass is safe.
|
|
159
|
-
*
|
|
160
|
-
* Throws for an unknown `evidenceId`: a caller attributing evidence this
|
|
161
|
-
* store does not hold has a bug, and writing nothing would hide it.
|
|
134
|
+
* Refuses to re-attribute to a different node: one observation can
|
|
135
|
+
* genuinely name two graph entities (RT-052), and that's expressed as an
|
|
136
|
+
* `EvidenceAttribution` alongside the evidence, not by overwriting the
|
|
137
|
+
* stored one. Idempotent for the same node. Throws for an unknown
|
|
138
|
+
* `evidenceId` — a caller bug, not something to write around silently.
|
|
162
139
|
*/
|
|
163
140
|
attribute(evidenceId: string, graphNodeId: string): void;
|
|
164
141
|
/**
|
|
165
|
-
* The general query surface
|
|
166
|
-
*
|
|
167
|
-
* Before this, the store answered two questions — by id and by execution —
|
|
168
|
-
* and §19 asks for six axes: trace, service, graph node, source location,
|
|
169
|
-
* time, and execution. **Five of the six did not exist**, which is
|
|
170
|
-
* invisible from "evidence persistence works" and was found by auditing
|
|
171
|
-
* the checklist rather than by anything failing.
|
|
142
|
+
* The general query surface. Before this, the store answered two
|
|
143
|
+
* questions (by id, by execution); five of six needed axes didn't exist.
|
|
172
144
|
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* trace id and quietly receive a prior run's evidence too. Making the
|
|
179
|
-
* field optional-with-a-default would mean the safe behaviour depends on
|
|
180
|
-
* remembering to pass something, which is how the default gets forgotten
|
|
181
|
-
* exactly once. Passing `null` is a deliberate statement that a
|
|
182
|
-
* cross-execution answer is wanted — the same discipline
|
|
183
|
-
* `resolveSymbolNode` applies to `repo`, where there is no honest default
|
|
184
|
-
* either.
|
|
145
|
+
* `executionId` is required and may be explicitly `null` — the
|
|
146
|
+
* load-bearing choice here. Making it optional-with-a-default risks a
|
|
147
|
+
* caller forgetting it and silently leaking evidence across executions;
|
|
148
|
+
* passing `null` is a deliberate statement that a cross-execution answer
|
|
149
|
+
* is wanted, same discipline `resolveSymbolNode` applies to `repo`.
|
|
185
150
|
*
|
|
186
|
-
* Criteria combine with AND.
|
|
187
|
-
*
|
|
188
|
-
* and is spelled out rather than reachable by accident.
|
|
151
|
+
* Criteria combine with AND. Empty criteria + null execution returns
|
|
152
|
+
* everything — a real request (store dump), spelled out on purpose.
|
|
189
153
|
*/
|
|
190
154
|
query(criteria: EvidenceQuery): Evidence[];
|
|
191
155
|
/**
|
|
192
|
-
* Deletes
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* decides when retention actually runs. Throws rather than silently
|
|
197
|
-
* no-op-ing if `retentionMs` was never configured: a caller explicitly
|
|
198
|
-
* asking to purge with no policy in place is a configuration mistake
|
|
199
|
-
* worth surfacing, not masking.
|
|
156
|
+
* Deletes rows older than `retentionMs` relative to `now` (overridable
|
|
157
|
+
* for tests). Never called automatically. Throws rather than no-op-ing
|
|
158
|
+
* if `retentionMs` was never configured — purging with no policy is a
|
|
159
|
+
* config mistake worth surfacing.
|
|
200
160
|
*/
|
|
201
161
|
purgeExpired(now?: Date): number;
|
|
202
162
|
close(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evidence-store.d.ts","sourceRoot":"","sources":["../src/evidence-store.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"evidence-store.d.ts","sourceRoot":"","sources":["../src/evidence-store.ts"],"names":[],"mappings":"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"}
|