@voltro/plugin-audit 0.32.0 → 0.34.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/index.d.ts CHANGED
@@ -43,6 +43,68 @@ export declare const auditBySubject: (store: AuditQueryStore, subjectId: string,
43
43
  */
44
44
  export declare const auditByTrace: (store: AuditQueryStore, traceId: string) => Promise<ReadonlyArray<Record<string, unknown>>>;
45
45
 
46
+ /** A writer's chain state. One per `dataStoreAuditSink`, i.e. one per process. */
47
+ export declare interface AuditChain {
48
+ readonly chainId: string;
49
+ /**
50
+ * Allocate this row's link and stamp it onto the row. SYNCHRONOUS and
51
+ * `await`-free by construction — that is the whole concurrency argument (see
52
+ * the header). Do not make it async.
53
+ */
54
+ readonly link: (row: Record<string, unknown>) => Record<string, unknown>;
55
+ }
56
+
57
+ export declare interface AuditChainIssue {
58
+ readonly kind: AuditChainIssueKind;
59
+ readonly chainId: string;
60
+ readonly seq: number;
61
+ /** The offending row's id, when it has one (a `gap` names the MISSING seq,
62
+ * so it has no row and no id). */
63
+ readonly rowId?: string;
64
+ readonly detail: string;
65
+ }
66
+
67
+ export declare type AuditChainIssueKind = 'tampered' | 'broken-link' | 'gap';
68
+
69
+ /** The columns the chain adds to `_voltro_audit_log`. */
70
+ export declare interface AuditChainLink {
71
+ /** Which writer's chain this row belongs to. Null for an unchained row. */
72
+ readonly chainId: string;
73
+ /** 1-based position within `chainId`. */
74
+ readonly seq: number;
75
+ /** The previous row's `hash`, or null for the first row of a chain. */
76
+ readonly prevHash: string | null;
77
+ /** `H(chainId, seq, prevHash, content)` — hex. */
78
+ readonly hash: string;
79
+ }
80
+
81
+ export declare interface AuditChainSummary {
82
+ readonly chainId: string;
83
+ /** Lowest `seq` present. `> 1` means the retention sweep pruned a prefix. */
84
+ readonly from: number;
85
+ /** Highest `seq` present. */
86
+ readonly to: number;
87
+ /** The tip hash — publish this somewhere append-only to defeat tail
88
+ * truncation and to bind an unkeyed chain to a point in time. */
89
+ readonly tip: string;
90
+ /** Whether the chain starts above `seq` 1 (a pruned prefix, not an issue). */
91
+ readonly prunedPrefix: boolean;
92
+ }
93
+
94
+ export declare interface AuditChainVerification {
95
+ readonly ok: boolean;
96
+ readonly rowsChecked: number;
97
+ /** Rows carrying no `hash` — written before chaining shipped, or stripped.
98
+ * Not an `issue` (a pre-chain deployment is not tampering) but never
99
+ * silently dropped either. */
100
+ readonly unchainedRows: number;
101
+ readonly chains: ReadonlyArray<AuditChainSummary>;
102
+ readonly issues: ReadonlyArray<AuditChainIssue>;
103
+ /** Whether the digest was keyed. An `ok: true` from an UNKEYED run is a
104
+ * weaker statement — surfaced so a report cannot quietly overstate it. */
105
+ readonly keyed: boolean;
106
+ }
107
+
46
108
  /** Narrow slice of the framework DataStore the durable sink needs. */
47
109
  export declare interface AuditDataStore {
48
110
  insert: (table: string, row: Record<string, unknown>) => Promise<unknown>;
@@ -113,6 +175,35 @@ export declare const auditLogTables: ReadonlyArray<AuditLogTable>;
113
175
  export declare const auditPlugin: (options?: AuditPluginOptions) => VoltroPlugin;
114
176
 
115
177
  export declare interface AuditPluginOptions {
178
+ /**
179
+ * Namespace for this plugin's inspect surface + any rpc tags it contributes. Default `audit`.
180
+ *
181
+ * Set it when your app already publishes under that name — an exact tag
182
+ * collision is fatal at codegen, and this is the way out.
183
+ */
184
+ readonly alias?: string;
185
+ /**
186
+ * Contribute this plugin's tables via `extendSchema.tables`. Default `true`.
187
+ *
188
+ * Set `false` when your app ALREADY declares equivalent tables and you want to
189
+ * keep them — the seam this exists for. The plugin then contributes no DDL and
190
+ * the declarative differ never proposes its tables; everything else (routes,
191
+ * inspect, interceptors) is unchanged.
192
+ *
193
+ * **What you take over, exactly:** a table named `_voltro_audit_log` with the shape
194
+ * `auditLogTable` declares (exported from this package, so declare it with
195
+ * `.renamedFrom()` or copy its columns). The `datastore` sink writes to it BY
196
+ * NAME through the bound store; nothing validates that it exists, so a missing
197
+ * or mis-shaped table fails at the first audited mutation, not at boot.
198
+ *
199
+ * It is not offered on every plugin, and the omissions are deliberate rather
200
+ * than unfinished: a `tables: false` that quietly disables a table carrying an
201
+ * AUTHORIZATION or SAFETY decision — the SAML replay cache, SCIM provisioning
202
+ * state, billing's usage counters, cdc-out's outbox — is a security regression
203
+ * shipped as an ergonomics feature. Those plugins need a named store seam
204
+ * first, not a boolean.
205
+ */
206
+ readonly tables?: boolean;
116
207
  /**
117
208
  * Where to record audit events:
118
209
  * - 'console' (default) — pretty-prints to stdout for local dev.
@@ -336,6 +427,15 @@ export declare interface AuditQueryStore {
336
427
  query: (descriptor: unknown) => Promise<ReadonlyArray<Record<string, unknown>>>;
337
428
  }
338
429
 
430
+ /** `H(chainId, seq, prevHash, content)` for one row. Pure — the verifier and
431
+ * the writer call the SAME function, which is what makes the check mean
432
+ * anything. */
433
+ export declare const auditRowHash: (row: Record<string, unknown>, link: {
434
+ readonly chainId: string;
435
+ readonly seq: number;
436
+ readonly prevHash: string | null;
437
+ }) => string;
438
+
339
439
  export declare type AuditSink = (event: AuditEvent) => void | Promise<void> | Effect.Effect<void>;
340
440
 
341
441
  /** Drop everything in the in-process buffer. Useful between smoke phases. */
@@ -349,6 +449,10 @@ export declare const clearAuditBuffer: () => void;
349
449
  */
350
450
  export declare const dataStoreAuditSink: (store: AuditDataStore) => (event: AuditEvent) => Promise<void>;
351
451
 
452
+ /** Mint a fresh chain. `chainId` is 96 bits of CSPRNG — collisions across
453
+ * replicas would merge two chains into one apparent fork. */
454
+ export declare const makeAuditChain: (chainId?: string) => AuditChain;
455
+
352
456
  /**
353
457
  * Read the in-process audit buffer. Returns a stable snapshot copy —
354
458
  * mutating it has no effect on the running buffer. Empty array when
@@ -356,6 +460,23 @@ export declare const dataStoreAuditSink: (store: AuditDataStore) => (event: Audi
356
460
  */
357
461
  export declare const readAuditBuffer: () => ReadonlyArray<AuditEvent>;
358
462
 
463
+ /**
464
+ * Recompute every chain in `_voltro_audit_log` and report what does not add up.
465
+ *
466
+ * Reads through the same `AuditQueryStore` the other read entry points use, so
467
+ * it works against any dialect and inside a transaction. Ordered by
468
+ * `(chainId, seq)` — the `byAuditChain` index exists for exactly this scan.
469
+ */
470
+ export declare const verifyAuditChain: (store: AuditQueryStore, options?: VerifyAuditChainOptions) => Promise<AuditChainVerification>;
471
+
472
+ export declare interface VerifyAuditChainOptions {
473
+ /** Verify one writer's chain instead of all of them. */
474
+ readonly chainId?: string;
475
+ /** Cap the rows read. Unset = every row, which is the honest default for a
476
+ * completeness check; pass one for a spot check on a very large table. */
477
+ readonly limit?: number;
478
+ }
479
+
359
480
  export { VoltroPlugin }
360
481
 
361
482
  export { }
package/dist/index.js CHANGED
@@ -1,25 +1,142 @@
1
1
  import { audit as e } from "./mixin.js";
2
2
  import { Effect as t } from "effect";
3
- import { definePlugin as n } from "@voltro/protocol";
4
- import { createLogger as r } from "@voltro/logger";
5
- import { and as i, eq as a, id as o, json as s, registerRetention as c, resolveActorSnapshot as l, retentionTtlMsFromEnv as u, table as d, text as f, timestamp as p } from "@voltro/database";
6
- //#region src/store.ts
7
- var m = "_voltro_audit_log", h = d(m, {
8
- id: o({ prefix: "audit" }),
9
- tag: f(),
10
- at: p(),
11
- subjectId: f().nullable(),
12
- tenantId: f().nullable(),
13
- traceId: f(),
14
- status: f(),
15
- durationMs: f(),
16
- subject: s(),
17
- actor: s().nullable(),
18
- scope: s().nullable(),
19
- metadata: s().nullable(),
20
- input: s(),
21
- outcome: s(),
22
- errorTag: f().nullable()
3
+ import { definePlugin as n, pluginInstanceName as r } from "@voltro/protocol";
4
+ import { createLogger as i } from "@voltro/logger";
5
+ import { and as a, eq as o, id as s, integer as c, json as l, registerRetention as u, resolveActorSnapshot as d, retentionTtlMsFromEnv as f, table as p, text as m, timestamp as h } from "@voltro/database";
6
+ import { createHash as g, createHmac as _, randomBytes as v } from "node:crypto";
7
+ //#region src/tableName.ts
8
+ var y = "_voltro_audit_log", b = [
9
+ "tag",
10
+ "at",
11
+ "subjectId",
12
+ "tenantId",
13
+ "traceId",
14
+ "status",
15
+ "durationMs",
16
+ "subject",
17
+ "actor",
18
+ "scope",
19
+ "metadata",
20
+ "input",
21
+ "outcome",
22
+ "errorTag"
23
+ ], x = (e) => {
24
+ if (e == null) return "null";
25
+ if (e instanceof Date) return JSON.stringify(e.toISOString());
26
+ let t = typeof e;
27
+ return t === "number" || t === "boolean" || t === "string" ? JSON.stringify(e) : t === "bigint" ? JSON.stringify(String(e)) : Array.isArray(e) ? `[${e.map(x).join(",")}]` : t === "object" ? `{${Object.entries(e).filter(([, e]) => e !== void 0).sort(([e], [t]) => e < t ? -1 : +(e > t)).map(([e, t]) => `${JSON.stringify(e)}:${x(t)}`).join(",")}}` : JSON.stringify(String(e));
28
+ }, S = (e) => e instanceof Date ? e.toISOString() : e, C = (e) => x(Object.fromEntries(b.map((t) => [t, S(e[t] ?? null)]))), w = (e) => {
29
+ let t = process.env.VOLTRO_AUDIT_CHAIN_SECRET;
30
+ return t ? _("sha256", t).update(e, "utf8").digest("hex") : g("sha256").update(e, "utf8").digest("hex");
31
+ }, T = (e, t) => w(JSON.stringify([
32
+ t.chainId,
33
+ t.seq,
34
+ t.prevHash,
35
+ C(e)
36
+ ])), E = (e = v(12).toString("base64url")) => {
37
+ let t = 0, n = null;
38
+ return {
39
+ chainId: e,
40
+ link: (r) => {
41
+ t += 1;
42
+ let i = T(r, {
43
+ chainId: e,
44
+ seq: t,
45
+ prevHash: n
46
+ }), a = {
47
+ ...r,
48
+ chainId: e,
49
+ seq: t,
50
+ prevHash: n,
51
+ hash: i
52
+ };
53
+ return n = i, a;
54
+ }
55
+ };
56
+ }, D = async (e, t = {}) => {
57
+ let n = t.chainId === void 0 ? void 0 : o("chainId", t.chainId), r = await e.query({
58
+ table: y,
59
+ ...n ? { predicate: n } : {},
60
+ order: [{
61
+ column: "chainId",
62
+ direction: "asc"
63
+ }, {
64
+ column: "seq",
65
+ direction: "asc"
66
+ }],
67
+ ...t.limit === void 0 ? {} : { take: t.limit }
68
+ }), i = [], a = [], s = 0, c = 0, l = null, u = () => {
69
+ l &&= (a.push({
70
+ chainId: l.chainId,
71
+ from: l.from,
72
+ to: l.last,
73
+ tip: l.tip,
74
+ prunedPrefix: l.from > 1
75
+ }), null);
76
+ };
77
+ for (let e of r) {
78
+ let t = e.chainId, n = e.hash, r = e.seq;
79
+ if (typeof t != "string" || typeof n != "string" || r == null) {
80
+ s += 1;
81
+ continue;
82
+ }
83
+ let a = Number(r), o = typeof e.prevHash == "string" ? e.prevHash : null, d = typeof e.id == "string" ? e.id : void 0;
84
+ c += 1, !l || l.chainId !== t ? (u(), l = {
85
+ chainId: t,
86
+ from: a,
87
+ last: a,
88
+ tip: n
89
+ }) : (a === l.last + 1 ? o !== l.tip && i.push({
90
+ kind: "broken-link",
91
+ chainId: t,
92
+ seq: a,
93
+ ...d ? { rowId: d } : {},
94
+ detail: `prevHash does not match the hash of seq ${l.last}`
95
+ }) : i.push({
96
+ kind: "gap",
97
+ chainId: t,
98
+ seq: l.last + 1,
99
+ detail: `chain jumps from seq ${l.last} to ${a} — ${a - l.last - 1} row(s) missing`
100
+ }), l.last = a, l.tip = n), T(e, {
101
+ chainId: t,
102
+ seq: a,
103
+ prevHash: o
104
+ }) !== n && i.push({
105
+ kind: "tampered",
106
+ chainId: t,
107
+ seq: a,
108
+ ...d ? { rowId: d } : {},
109
+ detail: "the row content does not hash to its stored hash — a column was altered"
110
+ });
111
+ }
112
+ return u(), {
113
+ ok: i.length === 0,
114
+ rowsChecked: c,
115
+ unchainedRows: s,
116
+ chains: a,
117
+ issues: i,
118
+ keyed: !!process.env.VOLTRO_AUDIT_CHAIN_SECRET
119
+ };
120
+ }, O = p(y, {
121
+ id: s({ prefix: "audit" }),
122
+ tag: m(),
123
+ at: h(),
124
+ subjectId: m().nullable(),
125
+ tenantId: m().nullable(),
126
+ traceId: m(),
127
+ status: m(),
128
+ durationMs: m(),
129
+ subject: l(),
130
+ actor: l().nullable(),
131
+ scope: l().nullable(),
132
+ metadata: l().nullable(),
133
+ input: l(),
134
+ outcome: l(),
135
+ errorTag: m().nullable(),
136
+ chainId: m().nullable(),
137
+ seq: c().nullable(),
138
+ prevHash: m().nullable(),
139
+ hash: m().nullable()
23
140
  }).index("byAuditTag", ["tag"]).index("byAuditTrace", ["traceId"]).index("byAuditSubjectStatus", [
24
141
  "subjectId",
25
142
  "status",
@@ -28,7 +145,7 @@ var m = "_voltro_audit_log", h = d(m, {
28
145
  "tenantId",
29
146
  "status",
30
147
  "at"
31
- ]), g = [h], _ = (e) => ({
148
+ ]).index("byAuditChain", ["chainId", "seq"]), k = [O], A = (e) => ({
32
149
  tag: e.tag,
33
150
  at: new Date(e.ts),
34
151
  subjectId: e.subject.id ?? null,
@@ -42,40 +159,43 @@ var m = "_voltro_audit_log", h = d(m, {
42
159
  metadata: e.metadata ?? null,
43
160
  input: e.input,
44
161
  outcome: e.outcome,
45
- errorTag: e.outcome.kind === "error" ? v(e.outcome.error) : null
46
- }), v = (e) => {
162
+ errorTag: e.outcome.kind === "error" ? j(e.outcome.error) : null
163
+ }), j = (e) => {
47
164
  if (typeof e != "object" || !e) return null;
48
165
  let t = e._tag;
49
166
  return typeof t == "string" && t !== "" ? t : null;
50
- }, y = async (e, t) => e.query({
51
- table: m,
52
- predicate: a("traceId", t),
167
+ }, M = async (e, t) => e.query({
168
+ table: y,
169
+ predicate: o("traceId", t),
53
170
  order: [{
54
171
  column: "at",
55
172
  direction: "asc"
56
173
  }]
57
- }), b = async (e, t, n = {}) => {
58
- let r = a("subjectId", t);
174
+ }), N = async (e, t, n = {}) => {
175
+ let r = o("subjectId", t);
59
176
  return e.query({
60
- table: m,
61
- predicate: n.status === void 0 ? r : i(r, a("status", n.status)),
177
+ table: y,
178
+ predicate: n.status === void 0 ? r : a(r, o("status", n.status)),
62
179
  order: [{
63
180
  column: "at",
64
181
  direction: "desc"
65
182
  }],
66
183
  take: n.limit ?? 100
67
184
  });
68
- }, x = (e) => async (t) => {
69
- let n = t.actor !== void 0 || typeof e.query != "function" ? t : {
70
- ...t,
71
- actor: await l(e, t.subject.id ?? null, t.subject.type)
185
+ }, P = (e) => {
186
+ let t = E();
187
+ return async (n) => {
188
+ let r = n.actor !== void 0 || typeof e.query != "function" ? n : {
189
+ ...n,
190
+ actor: await d(e, n.subject.id ?? null, n.subject.type)
191
+ };
192
+ await e.insert(y, t.link(A(r)));
72
193
  };
73
- await e.insert(m, _(n));
74
- }, S = r({ scope: "@voltro/plugin-audit" }), C = 1e3, w = [], T = () => [...w], E = () => {
75
- w.length = 0;
76
- }, D = (e) => {
77
- w.push(e), w.length > C && w.splice(0, w.length - C);
78
- }, O = (e) => {
194
+ }, F = i({ scope: "@voltro/plugin-audit" }), I = 1e3, L = [], R = () => [...L], z = () => {
195
+ L.length = 0;
196
+ }, B = (e) => {
197
+ L.push(e), L.length > I && L.splice(0, L.length - I);
198
+ }, V = (e) => {
79
199
  let t = e.subject.id ? `${e.subject.type}:${e.subject.id}` : e.subject.type, n = e.outcome.kind === "ok" ? "ok" : "error", r = {
80
200
  subject: t,
81
201
  tenant: e.subject.tenantId ?? null,
@@ -83,8 +203,8 @@ var m = "_voltro_audit_log", h = d(m, {
83
203
  durationMs: e.outcome.durationMs,
84
204
  traceId: e.traceId
85
205
  };
86
- n === "error" ? S.warn(e.tag, r) : S.info(e.tag, r);
87
- }, k = (e, n) => typeof e == "function" ? (n) => t.suspend(() => {
206
+ n === "error" ? F.warn(e.tag, r) : F.info(e.tag, r);
207
+ }, H = (e, n) => typeof e == "function" ? (n) => t.suspend(() => {
88
208
  let r = e(n);
89
209
  return r === void 0 ? t.void : t.isEffect(r) ? r : t.tryPromise({
90
210
  try: () => Promise.resolve(r),
@@ -96,7 +216,7 @@ var m = "_voltro_audit_log", h = d(m, {
96
216
  try: () => r(e),
97
217
  catch: (e) => e
98
218
  }) : t.void;
99
- }) : e === "memory" ? (e) => t.sync(() => D(e)) : (e) => t.sync(() => O(e)), A = (e) => {
219
+ }) : e === "memory" ? (e) => t.sync(() => B(e)) : (e) => t.sync(() => V(e)), U = (e) => {
100
220
  let t = [], n = [], r = (e) => {
101
221
  if (e._tag !== "Empty") {
102
222
  if (e._tag === "Fail") {
@@ -120,26 +240,27 @@ var m = "_voltro_audit_log", h = d(m, {
120
240
  }
121
241
  };
122
242
  return r(e), t[0] ?? n[0] ?? e;
123
- }, j = (e = {}) => {
124
- let r = e.sink === "datastore";
125
- r && c({
126
- table: m,
243
+ }, W = "@voltro/plugin-audit", G = (e = {}) => {
244
+ let i = e.sink === "datastore";
245
+ i && u({
246
+ source: "plugin",
247
+ table: y,
127
248
  timeColumn: "at",
128
- ttlMs: u(process.env.VOLTRO_AUDIT_LOG_TTL_HOURS, 24 * 365)
249
+ ttlMs: f(process.env.VOLTRO_AUDIT_LOG_TTL_HOURS, 24 * 365)
129
250
  });
130
- let i, a = k(e.sink, () => i), o = (t) => !(e.exclude?.test(t) || e.include && !e.include.test(t)), s = (t) => {
251
+ let a, o = H(e.sink, () => a), s = (e) => e === void 0 || !e.global && !e.sticky ? e : new RegExp(e.source, e.flags.replace(/[gy]/g, "")), c = s(e.exclude), l = s(e.include), d = (e) => !(c?.test(e) || l && !l.test(e)), p = (t) => {
131
252
  let n = e.record ?? "all";
132
253
  return n === "all" ? !0 : n === "errors" ? t.outcome.kind === "error" : n(t);
133
- }, l = { __redacted: "all" }, d = (t) => {
254
+ }, m = { __redacted: "all" }, h = (t) => {
134
255
  let n = e.redactInput ?? "all";
135
256
  return n === "none" ? t : {
136
257
  ...t,
137
- input: n === "all" ? l : n(t)
258
+ input: n === "all" ? m : n(t)
138
259
  };
139
- }, f = (t) => {
260
+ }, g = (t) => {
140
261
  let n = e.redactOutcome ?? "all";
141
262
  if (n === "none") return t;
142
- let r = n === "all" ? l : n(t), i = t.outcome;
263
+ let r = n === "all" ? m : n(t), i = t.outcome;
143
264
  if (i.kind === "ok") return {
144
265
  ...t,
145
266
  outcome: {
@@ -158,7 +279,7 @@ var m = "_voltro_audit_log", h = d(m, {
158
279
  }
159
280
  }
160
281
  };
161
- }, p = (t) => {
282
+ }, _ = (t) => {
162
283
  let n = e.redactSubject ?? "metadata";
163
284
  if (n === "none") return t;
164
285
  if (typeof n == "function") return {
@@ -172,18 +293,18 @@ var m = "_voltro_audit_log", h = d(m, {
172
293
  ...t,
173
294
  subject: {
174
295
  ...a,
175
- metadata: l
296
+ metadata: m
176
297
  }
177
298
  };
178
- }, h = (e) => s(e) ? a(f(p(d(e)))) : t.void, _ = (t) => {
299
+ }, v = (e) => p(e) ? o(g(_(h(e)))) : t.void, b = (t) => {
179
300
  if (e.resolveScope !== void 0) try {
180
301
  return e.resolveScope(t);
181
302
  } catch {
182
303
  return;
183
304
  }
184
- }, v = (e, n) => o(n.tag) ? t.suspend(() => {
185
- let r = Date.now(), i = _(n);
186
- return e.pipe(t.tap((e) => h({
305
+ }, x = (e, n) => d(n.tag) ? t.suspend(() => {
306
+ let r = Date.now(), i = b(n);
307
+ return e.pipe(t.tap((e) => v({
187
308
  ts: r,
188
309
  tag: n.tag,
189
310
  subject: n.subject,
@@ -195,7 +316,7 @@ var m = "_voltro_audit_log", h = d(m, {
195
316
  value: e,
196
317
  durationMs: Date.now() - r
197
318
  }
198
- }).pipe(t.catchAllCause(() => t.void))), t.tapErrorCause((e) => h({
319
+ }).pipe(t.catchAllCause(() => t.void))), t.tapErrorCause((e) => v({
199
320
  ts: r,
200
321
  tag: n.tag,
201
322
  subject: n.subject,
@@ -204,30 +325,34 @@ var m = "_voltro_audit_log", h = d(m, {
204
325
  input: n.input,
205
326
  outcome: {
206
327
  kind: "error",
207
- error: A(e),
328
+ error: U(e),
208
329
  durationMs: Date.now() - r
209
330
  }
210
331
  }).pipe(t.catchAllCause(() => t.void))));
211
- }) : e, y = v, b = v, S = v;
332
+ }) : e, S = x, C = x, w = x;
212
333
  return n({
213
- name: "@voltro/plugin-audit",
334
+ name: r({
335
+ base: W,
336
+ alias: e.alias
337
+ }),
338
+ baseName: W,
214
339
  description: "Records every mutation invocation; ships an audit() schema mixin for row-level metadata.",
215
340
  permissions: [
216
341
  "rpc:intercept:mutation",
217
342
  "rpc:intercept:action",
218
- ...r ? ["store:write"] : [],
343
+ ...i ? ["store:write"] : [],
219
344
  ...e.recordQueries === !0 ? ["rpc:intercept:query"] : []
220
345
  ],
221
- ...r ? {
222
- extendSchema: { tables: g },
346
+ ...i ? {
347
+ ...e.tables === !1 ? {} : { extendSchema: { tables: k } },
223
348
  bindDataStore: (e) => {
224
- i = x(e);
349
+ a = P(e);
225
350
  }
226
351
  } : {},
227
- interceptMutation: y,
228
- interceptAction: b,
229
- ...e.recordQueries === !0 ? { interceptQuery: S } : {}
352
+ interceptMutation: S,
353
+ interceptAction: C,
354
+ ...e.recordQueries === !0 ? { interceptQuery: w } : {}
230
355
  });
231
356
  };
232
357
  //#endregion
233
- export { m as AUDIT_LOG_TABLE, e as audit, b as auditBySubject, y as auditByTrace, _ as auditEventToRow, h as auditLogTable, g as auditLogTables, j as auditPlugin, E as clearAuditBuffer, x as dataStoreAuditSink, T as readAuditBuffer };
358
+ export { y as AUDIT_LOG_TABLE, e as audit, N as auditBySubject, M as auditByTrace, A as auditEventToRow, O as auditLogTable, k as auditLogTables, G as auditPlugin, T as auditRowHash, z as clearAuditBuffer, P as dataStoreAuditSink, E as makeAuditChain, R as readAuditBuffer, D as verifyAuditChain };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/plugin-audit",
3
- "version": "0.32.0",
3
+ "version": "0.34.0",
4
4
  "description": "Audit plugin — ships the `audit()` schema mixin (createdAt/updatedAt/createdBy/updatedBy → Actor) plus an optional mutation interceptor that records every call to a configurable sink (console / memory / custom function).",
5
5
  "keywords": [
6
6
  "voltro",
@@ -27,7 +27,8 @@
27
27
  "types": "./dist/mixin.d.ts",
28
28
  "import": "./dist/mixin.js",
29
29
  "default": "./dist/mixin.js"
30
- }
30
+ },
31
+ "./package.json": "./package.json"
31
32
  },
32
33
  "main": "./dist/index.js",
33
34
  "module": "./dist/index.js",
@@ -37,9 +38,9 @@
37
38
  "node": ">=24.0.0"
38
39
  },
39
40
  "dependencies": {
40
- "@voltro/database": "0.32.0",
41
- "@voltro/logger": "0.32.0",
42
- "@voltro/protocol": "0.32.0"
41
+ "@voltro/database": "0.34.0",
42
+ "@voltro/logger": "0.34.0",
43
+ "@voltro/protocol": "0.34.0"
43
44
  },
44
45
  "peerDependencies": {
45
46
  "effect": "^3.22.0"