@bli-cockpit/telemetry-core 0.1.23 → 0.1.24

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.
@@ -54,6 +54,7 @@ export declare const RawEvidenceLegacyUploadResponseSchema: z.ZodObject<{
54
54
  sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
55
55
  original_byte_size: z.ZodOptional<z.ZodNumber>;
56
56
  sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
57
+ applied_at: z.ZodOptional<z.ZodString>;
57
58
  }, z.core.$strict>>;
58
59
  }, z.core.$strict>>;
59
60
  }, z.core.$strict>;
@@ -119,6 +120,7 @@ export declare const RawEvidenceUploadBeginObjectSchema: z.ZodObject<{
119
120
  sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
120
121
  original_byte_size: z.ZodOptional<z.ZodNumber>;
121
122
  sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
123
+ applied_at: z.ZodOptional<z.ZodString>;
122
124
  }, z.core.$strict>>;
123
125
  }, z.core.$strict>;
124
126
  chunk_size_bytes: z.ZodNumber;
@@ -218,6 +220,7 @@ export declare const RawEvidenceUploadBeginRequestSchema: z.ZodObject<{
218
220
  sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
219
221
  original_byte_size: z.ZodOptional<z.ZodNumber>;
220
222
  sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
223
+ applied_at: z.ZodOptional<z.ZodString>;
221
224
  }, z.core.$strict>>;
222
225
  }, z.core.$strict>;
223
226
  chunk_size_bytes: z.ZodNumber;
@@ -386,6 +389,7 @@ export declare const RawEvidenceUploadCommitResponseSchema: z.ZodObject<{
386
389
  sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
387
390
  original_byte_size: z.ZodOptional<z.ZodNumber>;
388
391
  sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
392
+ applied_at: z.ZodOptional<z.ZodString>;
389
393
  }, z.core.$strict>>;
390
394
  }, z.core.$strict>;
391
395
  export type RawEvidenceUploadCommitResponse = z.infer<typeof RawEvidenceUploadCommitResponseSchema>;
@@ -316,6 +316,7 @@ export declare const TelemetryIngestEventDtoSchema: z.ZodObject<{
316
316
  sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
317
317
  original_byte_size: z.ZodOptional<z.ZodNumber>;
318
318
  sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
319
+ applied_at: z.ZodOptional<z.ZodString>;
319
320
  }, z.core.$strict>>;
320
321
  }, z.core.$strict>>>;
321
322
  otel: z.ZodOptional<z.ZodObject<{
@@ -792,6 +793,7 @@ export declare const TelemetryIngestEnvelopeSchema: z.ZodObject<{
792
793
  sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
793
794
  original_byte_size: z.ZodOptional<z.ZodNumber>;
794
795
  sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
796
+ applied_at: z.ZodOptional<z.ZodString>;
795
797
  }, z.core.$strict>>;
796
798
  }, z.core.$strict>>>;
797
799
  otel: z.ZodOptional<z.ZodObject<{
@@ -945,6 +947,7 @@ export declare const TelemetryIngestEnvelopeSchema: z.ZodObject<{
945
947
  sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
946
948
  original_byte_size: z.ZodOptional<z.ZodNumber>;
947
949
  sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
950
+ applied_at: z.ZodOptional<z.ZodString>;
948
951
  }, z.core.$strict>>;
949
952
  }, z.core.$strict>>>;
950
953
  }, z.core.$strict>>>;
package/dist/privacy.d.ts CHANGED
@@ -94,6 +94,7 @@ export declare const RawEvidencePointerSchema: z.ZodObject<{
94
94
  sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
95
95
  original_byte_size: z.ZodOptional<z.ZodNumber>;
96
96
  sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
97
+ applied_at: z.ZodOptional<z.ZodString>;
97
98
  }, z.core.$strict>>;
98
99
  }, z.core.$strict>;
99
100
  export type RawEvidencePointer = z.infer<typeof RawEvidencePointerSchema>;
@@ -86,8 +86,35 @@ export declare const RawEvidenceRedactionMetadataSchema: z.ZodObject<{
86
86
  sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
87
87
  original_byte_size: z.ZodOptional<z.ZodNumber>;
88
88
  sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
89
+ applied_at: z.ZodOptional<z.ZodString>;
89
90
  }, z.core.$strict>;
90
91
  export type RawEvidenceRedactionMetadata = z.infer<typeof RawEvidenceRedactionMetadataSchema>;
92
+ /**
93
+ * The receipt for a file the scan cleared (BLI-3277, moved here by BLI-3280).
94
+ *
95
+ * Same schema, same `mode` — the deterministic ruleset is what ran — with the
96
+ * verdict `scanned_clean` and zero of everything else. The content fields are
97
+ * the point: an evidence ref is only readable downstream when its redaction
98
+ * record hashes the bytes that are actually in the bucket, and for a clean file
99
+ * those are the original bytes, so both halves carry the same digest and size.
100
+ *
101
+ * It lives in telemetry-core rather than in the collector because two callers
102
+ * now build this exact record and they must never drift: the collector writes
103
+ * it at upload time, and the BLI-3280 operator backfill writes it onto refs
104
+ * that were stored before the receipt existed. Both must satisfy the same
105
+ * `sanitized_content_hash_sha256 === content_hash_sha256` /
106
+ * `sanitized_byte_size === byte_size` equality the digest reader checks, and a
107
+ * second copy of this function is a second chance to get that wrong.
108
+ *
109
+ * `appliedBy` is who ran the scan, and it is not decoration: a backfill passes
110
+ * `legacy_upload` so a reader can tell "the collector scanned this before it
111
+ * uploaded" apart from "an operator scanned the stored bytes afterwards".
112
+ */
113
+ export declare function scannedCleanRedactionMetadata(bytes: Uint8Array, options?: {
114
+ appliedBy?: RawEvidenceRedactionSource;
115
+ /** ISO-8601 with offset. Omitted entirely when absent (BLI-3290). */
116
+ appliedAt?: string;
117
+ }): RawEvidenceRedactionMetadata;
91
118
  export interface SecretRedactionResult {
92
119
  redacted: boolean;
93
120
  text: string;
@@ -1,4 +1,6 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { z } from "zod";
3
+ import { IsoDateTimeSchema } from "./common.js";
2
4
  /**
3
5
  * Secret-like content + file-name guards shared by the local collector
4
6
  * (attribution, collection) and the dashboard commit-time guard. Keeping a
@@ -181,6 +183,17 @@ export const RawEvidenceRedactionMetadataSchema = z
181
183
  sanitized_content_hash_sha256: z.string().regex(/^[a-f0-9]{64}$/).optional(),
182
184
  original_byte_size: z.number().int().nonnegative().optional(),
183
185
  sanitized_byte_size: z.number().int().nonnegative().optional(),
186
+ /**
187
+ * When the scan ran, ISO-8601 with an offset (BLI-3290).
188
+ *
189
+ * Optional, and it stays optional: every receipt written before this field
190
+ * existed is still valid, and the collector does not stamp it — at upload
191
+ * time the ref's own `received_at` already answers "when". It exists for
192
+ * receipts written over bytes that were stored long ago, where the ref's
193
+ * timestamps describe the ORIGINAL upload and nothing in the record would
194
+ * otherwise say when an operator re-scanned or re-masked the object.
195
+ */
196
+ applied_at: IsoDateTimeSchema.optional(),
184
197
  })
185
198
  .strict()
186
199
  // The per-status invariants the old `z.literal("sanitized")` + `.min(1)` +
@@ -253,6 +266,45 @@ export const RawEvidenceRedactionMetadataSchema = z
253
266
  });
254
267
  }
255
268
  });
269
+ /**
270
+ * The receipt for a file the scan cleared (BLI-3277, moved here by BLI-3280).
271
+ *
272
+ * Same schema, same `mode` — the deterministic ruleset is what ran — with the
273
+ * verdict `scanned_clean` and zero of everything else. The content fields are
274
+ * the point: an evidence ref is only readable downstream when its redaction
275
+ * record hashes the bytes that are actually in the bucket, and for a clean file
276
+ * those are the original bytes, so both halves carry the same digest and size.
277
+ *
278
+ * It lives in telemetry-core rather than in the collector because two callers
279
+ * now build this exact record and they must never drift: the collector writes
280
+ * it at upload time, and the BLI-3280 operator backfill writes it onto refs
281
+ * that were stored before the receipt existed. Both must satisfy the same
282
+ * `sanitized_content_hash_sha256 === content_hash_sha256` /
283
+ * `sanitized_byte_size === byte_size` equality the digest reader checks, and a
284
+ * second copy of this function is a second chance to get that wrong.
285
+ *
286
+ * `appliedBy` is who ran the scan, and it is not decoration: a backfill passes
287
+ * `legacy_upload` so a reader can tell "the collector scanned this before it
288
+ * uploaded" apart from "an operator scanned the stored bytes afterwards".
289
+ */
290
+ export function scannedCleanRedactionMetadata(bytes, options = {}) {
291
+ const digest = createHash("sha256").update(bytes).digest("hex");
292
+ return {
293
+ schema_version: "raw-evidence-redaction.v1",
294
+ status: "scanned_clean",
295
+ mode: "deterministic_text_replacement",
296
+ applied_by: [options.appliedBy ?? "local_collector"],
297
+ rule_counts: [],
298
+ secret_like_match_count: 0,
299
+ redacted_fields: [],
300
+ redacted_ranges: [],
301
+ original_content_hash_sha256: digest,
302
+ sanitized_content_hash_sha256: digest,
303
+ original_byte_size: bytes.byteLength,
304
+ sanitized_byte_size: bytes.byteLength,
305
+ ...(options.appliedAt === undefined ? {} : { applied_at: options.appliedAt }),
306
+ };
307
+ }
256
308
  // Same fence as the detector (BLI-3116) — the two must agree or a prefixed
257
309
  // name would be detected and then left unredacted, which costs whole sessions
258
310
  // (see findSecretRedactionMatches).
@@ -102,6 +102,7 @@ export declare const SourceScanEventSummarySchema: z.ZodObject<{
102
102
  sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
103
103
  original_byte_size: z.ZodOptional<z.ZodNumber>;
104
104
  sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
105
+ applied_at: z.ZodOptional<z.ZodString>;
105
106
  }, z.core.$strict>>;
106
107
  }, z.core.$strict>>>;
107
108
  }, z.core.$strict>;
@@ -219,6 +220,7 @@ export declare const SourceScanResultSchema: z.ZodObject<{
219
220
  sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
220
221
  original_byte_size: z.ZodOptional<z.ZodNumber>;
221
222
  sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
223
+ applied_at: z.ZodOptional<z.ZodString>;
222
224
  }, z.core.$strict>>;
223
225
  }, z.core.$strict>>>;
224
226
  }, z.core.$strict>>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bli-cockpit/telemetry-core",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",