@volidator/node 1.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/README.md +518 -0
- package/dist/chunk-KEXIKZGN.js +56 -0
- package/dist/chunk-KEXIKZGN.js.map +1 -0
- package/dist/chunk-YI4DCCHT.js +663 -0
- package/dist/chunk-YI4DCCHT.js.map +1 -0
- package/dist/index.cjs +689 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +250 -0
- package/dist/index.d.ts +250 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/next.cjs +735 -0
- package/dist/middleware/next.cjs.map +1 -0
- package/dist/middleware/next.d.cts +12 -0
- package/dist/middleware/next.d.ts +12 -0
- package/dist/middleware/next.js +8 -0
- package/dist/middleware/next.js.map +1 -0
- package/dist/plugins/clerk.cjs +762 -0
- package/dist/plugins/clerk.cjs.map +1 -0
- package/dist/plugins/clerk.d.cts +15 -0
- package/dist/plugins/clerk.d.ts +15 -0
- package/dist/plugins/clerk.js +35 -0
- package/dist/plugins/clerk.js.map +1 -0
- package/dist/plugins/universal.cjs +774 -0
- package/dist/plugins/universal.cjs.map +1 -0
- package/dist/plugins/universal.d.cts +22 -0
- package/dist/plugins/universal.d.ts +22 -0
- package/dist/plugins/universal.js +47 -0
- package/dist/plugins/universal.js.map +1 -0
- package/package.json +74 -0
package/README.md
ADDED
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
# Volidator Node.js SDK
|
|
2
|
+
|
|
3
|
+
**Zero-knowledge, server-blind audit logging for Node.js, Next.js, and edge runtimes.**
|
|
4
|
+
|
|
5
|
+
Volidator encrypts every log entry locally — on your server — before sending it. The Volidator backend stores only ciphertext and blind indexes. Your plaintext data never leaves your infrastructure unencrypted.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@volidator/node)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## What is Volidator?
|
|
13
|
+
|
|
14
|
+
**Volidator** is the developer-first, zero-knowledge audit log infrastructure built for modern applications, enterprise B2B SaaS, and autonomous AI agents. By utilizing local AES-256-GCM encryption and blind indexing (HMAC-SHA-256) on your servers before ingestion, Volidator allows you to store, query, and stream audit trails without ever holding or exposing raw PII (Personally Identifiable Information) or sensitive activity data (though configurable if you want to host PII/PHI with us).
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Why Volidator? (Pain Points We Solve)
|
|
19
|
+
|
|
20
|
+
Traditional logging tools force a dangerous compromise: either send raw customer data to a third-party logging vendor (creating security liabilities, compliance issues, and API keys leakage risks), or spend months building a custom, secure compliance database in-house.
|
|
21
|
+
|
|
22
|
+
Volidator solves this with **Zero-Knowledge Audit Trails**:
|
|
23
|
+
* **Enterprise Compliance in Minutes:** Unlocks enterprise-ready audit logging matching SOC 2 (CC6.x), ISO 27001 (A.12.4), HIPAA, and GDPR standards under 5 minutes.
|
|
24
|
+
* **Zero Trust Security:** Even if Volidator's databases were compromised, hackers see only randomized ciphertext. Decryption keys live exclusively in your server environment variables and the user's browser hash fragments.
|
|
25
|
+
* **AI Agent Action Auditing & Accountability:** Autonomous AI agents make decisions, run API calls, and modify databases. Volidator provides a tamper-proof ledger to trace exactly *which* agent tool was executed, *why* (LLM prompt/context), and *what* was modified, satisfying critical alignment and security monitoring requirements.
|
|
26
|
+
* **Instant Customer-Facing Dashboards:** Embed fully-interactive, securely hydrated log tables inside your React frontends using our signed JIT tokens.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Who is it for?
|
|
31
|
+
|
|
32
|
+
* **B2B SaaS Engineering Teams:** Developers who need to provide enterprise tenants with search, filter, and CSV exports of system events.
|
|
33
|
+
* **Security & Compliance Teams:** Organizations aiming to achieve security certifications without expanding their data privacy liability or telemetry footprint.
|
|
34
|
+
* **AI & Agentic App Developers:** Builders establishing guardrails and debug logs for autonomous systems to audit agent decisions, LLM outputs, and automated tool calls.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Table of Contents
|
|
39
|
+
|
|
40
|
+
1. [Install](#1-install)
|
|
41
|
+
2. [Environment Setup](#2-environment-setup)
|
|
42
|
+
3. [Initialize the Client](#3-initialize-the-client)
|
|
43
|
+
4. [Log an Event](#4-log-an-event)
|
|
44
|
+
5. [Pass Request Context](#5-pass-request-context)
|
|
45
|
+
6. [Telemetry Configuration](#6-telemetry-configuration)
|
|
46
|
+
7. [PII Redaction](#7-pii-redaction)
|
|
47
|
+
8. [JIT Hydration (referenceKeys)](#8-jit-hydration-referencekeys)
|
|
48
|
+
9. [Keyring Rotation](#9-keyring-rotation)
|
|
49
|
+
10. [Embed Token](#10-embed-token)
|
|
50
|
+
11. [Compliance Helpers](#11-compliance-helpers)
|
|
51
|
+
12. [Next.js Middleware](#12-nextjs-middleware)
|
|
52
|
+
14. [`@volidator/react`](#14-volidatorreact)
|
|
53
|
+
15. [AI Agent Auditing (VolidatorAgent)](#15-ai-agent-auditing-volidatoragent)
|
|
54
|
+
16. [Batch Ingestion (logBatch)](#16-batch-ingestion-logbatch)
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 1. Install
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install @volidator/node
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 2. Environment Setup
|
|
67
|
+
|
|
68
|
+
Store secrets as environment variables. Never hardcode them.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Authenticates your server with the Volidator ingestion endpoint
|
|
72
|
+
VOLIDATOR_API_KEY="val_live_xxxxxxxx..."
|
|
73
|
+
|
|
74
|
+
# AES-256-GCM encryption key — your data is encrypted with this before leaving your server
|
|
75
|
+
VOLIDATOR_ENCRYPTION_KEY="vol-dek-xxxxxxxx..."
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 3. Initialize the Client
|
|
81
|
+
|
|
82
|
+
Create **one instance** per application and reuse it. Do not instantiate per-request.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { VolidatorClient } from "@volidator/node";
|
|
86
|
+
|
|
87
|
+
export const volidator = new VolidatorClient({
|
|
88
|
+
apiKey: process.env.VOLIDATOR_API_KEY!,
|
|
89
|
+
encryptionKey: process.env.VOLIDATOR_ENCRYPTION_KEY!,
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 4. Log an Event
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
await volidator.log({
|
|
99
|
+
actor: "usr_12345", // Who performed the action
|
|
100
|
+
action: "user.login.success", // What happened (use dot notation)
|
|
101
|
+
target: "workspace_abc789", // What was affected
|
|
102
|
+
metadata: {
|
|
103
|
+
deviceName: "MacBook Pro",
|
|
104
|
+
authProvider: "Google OAuth",
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`log()` returns `true` if the event was accepted, `false` if delivery failed. It never throws — failures are swallowed and logged to `console.error`.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 5. Pass Request Context
|
|
114
|
+
|
|
115
|
+
Pass your framework's `Request` object directly. The SDK extracts IP, User-Agent, and geolocation headers automatically.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// Next.js App Router / Cloudflare Workers / Express
|
|
119
|
+
await volidator.log({
|
|
120
|
+
actor: session.userId,
|
|
121
|
+
action: "settings.update",
|
|
122
|
+
req: request, // Web API Request or Node.js IncomingMessage
|
|
123
|
+
metadata: { fieldChanged: "email_address" },
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Supported request formats:
|
|
128
|
+
- `Request` — Next.js App Router, Cloudflare Workers, Bun
|
|
129
|
+
- `IncomingMessage` — Node.js `http`, Express, Fastify
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 6. Telemetry Configuration
|
|
134
|
+
|
|
135
|
+
Control how IP, User-Agent, and location data are handled. Choose a preset, then override individual fields as needed.
|
|
136
|
+
|
|
137
|
+
| Preset | IP | User-Agent | Location |
|
|
138
|
+
|---|---|---|---|
|
|
139
|
+
| `"strict"` | skip | skip | disabled |
|
|
140
|
+
| `"standard"` *(default)* | anonymized (hashed) | parsed to browser/OS | country + region |
|
|
141
|
+
| `"full"` | stored as-is | stored as-is | country + region + city |
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
const volidator = new VolidatorClient({
|
|
145
|
+
apiKey: process.env.VOLIDATOR_API_KEY!,
|
|
146
|
+
encryptionKey: process.env.VOLIDATOR_ENCRYPTION_KEY!,
|
|
147
|
+
telemetry: {
|
|
148
|
+
preset: "standard", // baseline
|
|
149
|
+
ip: "skip", // override: don't store IP at all
|
|
150
|
+
location: false, // override: disable geolocation
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 7. PII Redaction
|
|
158
|
+
|
|
159
|
+
For fields containing sensitive data that should never be stored — even in encrypted form — use `redactKeys`. The value is replaced with `[REDACTED:fieldName]` **before** encryption.
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const volidator = new VolidatorClient({
|
|
163
|
+
apiKey: process.env.VOLIDATOR_API_KEY!,
|
|
164
|
+
encryptionKey: process.env.VOLIDATOR_ENCRYPTION_KEY!,
|
|
165
|
+
redactKeys: ["actor", "metadata.socialSecurityNumber", "metadata.email"],
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Supported key patterns:
|
|
170
|
+
- `"actor"` — the top-level actor field
|
|
171
|
+
- `"target"` — the top-level target field
|
|
172
|
+
- `"metadata.fieldName"` — any metadata field by name
|
|
173
|
+
|
|
174
|
+
> **Blind indexes are still computed from the original value before redaction**, so the Volidator dashboard can still filter and search by actor/target even after redaction.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 8. JIT Hydration (`referenceKeys`)
|
|
179
|
+
|
|
180
|
+
JIT (Just-In-Time) Hydration lets you store a **non-sensitive reference ID** instead of PII, while preserving full dashboard display names. PII never reaches Volidator's servers.
|
|
181
|
+
|
|
182
|
+
When you configure `referenceKeys`, you pass fields as `{ id, pii }` objects:
|
|
183
|
+
- `id` — the internal identifier stored as `[REF:id]` in the encrypted log
|
|
184
|
+
- `pii` — the real value used **only** to compute the blind index, then discarded
|
|
185
|
+
|
|
186
|
+
The dashboard resolves `[REF:id]` to a display name at render time by sending a `postMessage` request back to your application. See the [`@volidator/react`](#14-volidatorreact) section for the client-side hook.
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const volidator = new VolidatorClient({
|
|
190
|
+
apiKey: process.env.VOLIDATOR_API_KEY!,
|
|
191
|
+
encryptionKey: process.env.VOLIDATOR_ENCRYPTION_KEY!,
|
|
192
|
+
referenceKeys: ["actor"],
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
await volidator.log({
|
|
196
|
+
// Pass { id, pii } for any field in referenceKeys
|
|
197
|
+
actor: { id: "usr_890", pii: "alice@company.com" },
|
|
198
|
+
action: "document.deleted",
|
|
199
|
+
target: "doc_4521",
|
|
200
|
+
});
|
|
201
|
+
// Stored: actor = "[REF:usr_890]"
|
|
202
|
+
// Blind index computed from "alice@company.com" — search still works
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`referenceKeys` and `redactKeys` can coexist. If a field is in both, `referenceKeys` takes precedence.
|
|
206
|
+
|
|
207
|
+
→ [Full JIT Hydration guide](https://docs.volidator.com/guides/jit-hydration/)
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 9. Keyring Rotation
|
|
212
|
+
|
|
213
|
+
Rotate your encryption key without re-encrypting historical logs. Provide a `keyring` (all active key versions) and `activeEncryptionKeyId` (the key used for new writes). Old logs are decrypted with whichever key was active when they were written.
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
const volidator = new VolidatorClient({
|
|
217
|
+
apiKey: process.env.VOLIDATOR_API_KEY!,
|
|
218
|
+
keyring: {
|
|
219
|
+
v1: process.env.VOLIDATOR_KEY_V1!, // old — decrypts historical logs
|
|
220
|
+
v2: process.env.VOLIDATOR_KEY_V2!, // new — encrypts all new writes
|
|
221
|
+
},
|
|
222
|
+
activeEncryptionKeyId: "v2",
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Constraints:
|
|
227
|
+
- Keyring size is capped at **5 keys** (security + performance bound).
|
|
228
|
+
- `activeEncryptionKeyId` must be present in the `keyring` object.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## 10. Embed Token
|
|
233
|
+
|
|
234
|
+
Generate a signed JWT that scopes the embeddable dashboard widget to a specific actor, target, or tenant. This is called server-side; the returned `embedUrl` is dropped directly into an `<iframe>`.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const volidator = new VolidatorClient({
|
|
238
|
+
apiKey: process.env.VOLIDATOR_API_KEY!,
|
|
239
|
+
encryptionKey: process.env.VOLIDATOR_ENCRYPTION_KEY!,
|
|
240
|
+
// Required for generateEmbedToken():
|
|
241
|
+
projectId: process.env.VOLIDATOR_PROJECT_ID!,
|
|
242
|
+
clientSecret: process.env.VOLIDATOR_CLIENT_SECRET!,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const { embedUrl } = await volidator.generateEmbedToken({
|
|
246
|
+
actorId: session.userId, // Scope to this actor's logs
|
|
247
|
+
scope: "actor", // "actor" | "target" | "tenant" | "all" | "auditor"
|
|
248
|
+
expiresIn: "1h", // Max: 1h for actor/target/tenant; 7d for auditor
|
|
249
|
+
hostOrigin: "https://app.yourcompany.com", // Enables strict postMessage origin validation
|
|
250
|
+
view: {
|
|
251
|
+
columns: ["actor", "action", "metadata.ipAddress", "createdAt"],
|
|
252
|
+
defaultFilter: { action: "user.login" },
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// In your API route response:
|
|
257
|
+
return Response.json({ embedUrl });
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
// In your React component:
|
|
262
|
+
<iframe src={embedUrl} width="100%" height="600" />
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## 11. Compliance Helpers
|
|
268
|
+
|
|
269
|
+
`volidator.compliance` provides pre-tagged methods for common SOC 2 and ISO 27001 audit events. Each method automatically appends the correct `soc2_control` and `iso27001` keys to the log metadata.
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
// Access provisioning
|
|
273
|
+
await volidator.compliance.accessGranted({ actor: "admin_01", target: "usr_890" });
|
|
274
|
+
await volidator.compliance.accessRevoked({ actor: "admin_01", target: "usr_890" });
|
|
275
|
+
|
|
276
|
+
// Data movement
|
|
277
|
+
await volidator.compliance.dataExported({
|
|
278
|
+
actor: "usr_123",
|
|
279
|
+
metadata: { exportFormat: "csv", rowCount: 5420 },
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// System changes
|
|
283
|
+
await volidator.compliance.systemConfigChanged({
|
|
284
|
+
actor: "usr_123",
|
|
285
|
+
metadata: { setting: "mfa_enforcement", newValue: "required" },
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Authentication
|
|
289
|
+
await volidator.compliance.mfaEnabled({ actor: "usr_890" });
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
| Method | Action stored | SOC 2 | ISO 27001 |
|
|
293
|
+
|---|---|---|---|
|
|
294
|
+
| `accessGranted` | `access.granted` | CC6.1 | A.9.2.1 |
|
|
295
|
+
| `accessRevoked` | `access.revoked` | CC6.1 | A.9.2.6 |
|
|
296
|
+
| `dataExported` | `data.exported` | CC6.6 | A.12.4.1 |
|
|
297
|
+
| `systemConfigChanged` | `system.config_changed` | CC6.2 | A.12.1.2 |
|
|
298
|
+
| `mfaEnabled` | `mfa.enabled` | CC6.3 | A.9.4.2 |
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## 12. Next.js Middleware
|
|
303
|
+
|
|
304
|
+
The `withVolidator` wrapper injects a request-scoped `volidator` object into every Next.js App Router handler. It automatically extracts IP, User-Agent, and geolocation from the incoming request and merges it into every `log()` and `compliance.*()` call — no manual `req` passing needed.
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
import { withVolidator } from "@volidator/node/next";
|
|
308
|
+
import { volidator } from "@/lib/volidator";
|
|
309
|
+
|
|
310
|
+
export const POST = withVolidator(volidator, async (req: Request, ctx) => {
|
|
311
|
+
await ctx.volidator.log({
|
|
312
|
+
actor: session.userId,
|
|
313
|
+
action: "invoice.created",
|
|
314
|
+
target: invoiceId,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Compliance methods also receive full telemetry context:
|
|
318
|
+
await ctx.volidator.compliance.dataExported({ actor: session.userId });
|
|
319
|
+
|
|
320
|
+
return Response.json({ ok: true });
|
|
321
|
+
});
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## 13. Auth Plugins
|
|
327
|
+
|
|
328
|
+
Auth plugins extend `withVolidator` to automatically inject the authenticated user's ID as `actor` on every log call.
|
|
329
|
+
|
|
330
|
+
### Clerk
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import { createClerkAudit } from "@volidator/node/clerk";
|
|
334
|
+
import { auth } from "@clerk/nextjs/server";
|
|
335
|
+
import { volidator } from "@/lib/volidator";
|
|
336
|
+
|
|
337
|
+
const withClerkAudit = createClerkAudit({ client: volidator, getAuth: auth });
|
|
338
|
+
|
|
339
|
+
export const DELETE = withClerkAudit(async (req: Request, ctx) => {
|
|
340
|
+
// ctx.session — Clerk session object
|
|
341
|
+
// actor is automatically set to ctx.session.userId
|
|
342
|
+
await ctx.volidator.log({ action: "record.deleted", target: recordId });
|
|
343
|
+
return Response.json({ ok: true });
|
|
344
|
+
});
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Universal (Auth0, NextAuth, BetterAuth, Kinde, Supabase, ...)
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
import { createUniversalAudit } from "@volidator/node/universal";
|
|
351
|
+
import { getServerSession } from "next-auth";
|
|
352
|
+
import { volidator } from "@/lib/volidator";
|
|
353
|
+
|
|
354
|
+
const withAudit = createUniversalAudit({
|
|
355
|
+
client: volidator,
|
|
356
|
+
getSession: (req) => getServerSession(),
|
|
357
|
+
getUserId: (req, session) => session?.user?.id,
|
|
358
|
+
// Optionally inject extra metadata into every log call:
|
|
359
|
+
getMetadata: (req, session) => ({ userEmail: session?.user?.email }),
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
export const PUT = withAudit(async (req: Request, ctx) => {
|
|
363
|
+
await ctx.volidator.log({ action: "profile.updated" });
|
|
364
|
+
return Response.json({ ok: true });
|
|
365
|
+
});
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## 14. `@volidator/react`
|
|
371
|
+
|
|
372
|
+
The `@volidator/react` package provides the `useVolidatorHydration` hook, which manages the JIT Hydration postMessage handshake between your application and the Volidator embed iframe.
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
npm install @volidator/react
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
```tsx
|
|
379
|
+
import { useRef } from "react";
|
|
380
|
+
import { useVolidatorHydration } from "@volidator/react";
|
|
381
|
+
|
|
382
|
+
export function AuditLogPage({ embedUrl }: { embedUrl: string }) {
|
|
383
|
+
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
384
|
+
|
|
385
|
+
useVolidatorHydration({
|
|
386
|
+
iframeRef,
|
|
387
|
+
volidatorOrigin: "https://dash.volidator.com",
|
|
388
|
+
resolveActors: async (ids) => {
|
|
389
|
+
// Called with a deduplicated batch of reference IDs from decrypted logs.
|
|
390
|
+
// Return a map of id → { name, avatarUrl? }.
|
|
391
|
+
const res = await fetch("/api/users/resolve", {
|
|
392
|
+
method: "POST",
|
|
393
|
+
headers: { "Content-Type": "application/json" },
|
|
394
|
+
body: JSON.stringify({ ids }),
|
|
395
|
+
});
|
|
396
|
+
return res.json();
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
return <iframe ref={iframeRef} src={embedUrl} width="100%" height="600" />;
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
The hook:
|
|
405
|
+
1. Listens for `VOLIDATOR_RESOLVE_ACTORS` messages from the iframe.
|
|
406
|
+
2. Validates `event.origin` strictly against `volidatorOrigin` (wildcard `"*"` is not accepted).
|
|
407
|
+
3. Calls `resolveActors(ids)` with only the IDs not already in the local cache.
|
|
408
|
+
4. Posts the resolution map back into the iframe as `VOLIDATOR_RESOLVE_RESPONSE`.
|
|
409
|
+
5. Cleans up the event listener on component unmount.
|
|
410
|
+
|
|
411
|
+
→ [Full JIT Hydration guide](https://docs.volidator.com/guides/jit-hydration/)
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## 15. AI Agent Auditing (`VolidatorAgent`)
|
|
416
|
+
|
|
417
|
+
Volidator provides a specialized compliance logging namespace tailored for autonomous AI systems, LLMs, and multi-agent chains. Access these methods via `volidator.agent.*`.
|
|
418
|
+
|
|
419
|
+
Every call automatically appends the correct mapping metadata for **EU AI Act compliance**, **NIST AI RMF guidelines**, **SOC 2**, and **ISO 27001**.
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
// 1. Log an agent tool call or external API request
|
|
423
|
+
await volidator.agent.toolCall({
|
|
424
|
+
actor: "writer-agent-v1",
|
|
425
|
+
traceId: runId,
|
|
426
|
+
spanId: spanId,
|
|
427
|
+
toolName: "fetch_news",
|
|
428
|
+
toolInput: { topic: "AI Act updates" },
|
|
429
|
+
toolOutput: { results: ["..." ] },
|
|
430
|
+
success: true,
|
|
431
|
+
latencyMs: 140,
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// 2. Log an autonomous decision made by the model
|
|
435
|
+
await volidator.agent.decision({
|
|
436
|
+
actor: "writer-agent-v1",
|
|
437
|
+
traceId: runId,
|
|
438
|
+
decision: "publish_article",
|
|
439
|
+
rationale: "article scoring passed verification checks",
|
|
440
|
+
confidenceScore: 0.96,
|
|
441
|
+
modelId: "claude-3-5-sonnet",
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// 3. Request human oversight/escalation (EU AI Act Article 14)
|
|
445
|
+
await volidator.agent.escalation({
|
|
446
|
+
actor: "writer-agent-v1",
|
|
447
|
+
traceId: runId,
|
|
448
|
+
reason: "article output score fell below threshold",
|
|
449
|
+
urgency: "medium",
|
|
450
|
+
blockedAction: "auto_publish",
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// 4. Log suspected anomalies, injections, or system security events
|
|
454
|
+
await volidator.agent.anomaly({
|
|
455
|
+
actor: "guardrail-shield",
|
|
456
|
+
traceId: runId,
|
|
457
|
+
description: "jailbreak prompt pattern detected in input stream",
|
|
458
|
+
severity: "critical",
|
|
459
|
+
anomalyType: "prompt_injection",
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// 5. Log a model refusal based on safety alignment rules
|
|
463
|
+
await volidator.agent.refusal({
|
|
464
|
+
actor: "writer-agent-v1",
|
|
465
|
+
traceId: runId,
|
|
466
|
+
refusedInstruction: "write code to extract system logs",
|
|
467
|
+
reason: "corporate security policy alignment violation",
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// 6. Log execution context handoffs in multi-agent workflows
|
|
471
|
+
await volidator.agent.handoff({
|
|
472
|
+
actor: "orchestrator",
|
|
473
|
+
toAgentId: "designer-agent-v1",
|
|
474
|
+
instruction: "generate banner image matching text outline",
|
|
475
|
+
agentContext: { prompt: "neon style workspace banner" },
|
|
476
|
+
});
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Trace Correlation (OpenTelemetry Compatibility)
|
|
480
|
+
|
|
481
|
+
Pass standard trace metadata (`traceId`, `spanId`, `parentSpanId`) to map out parent-child relationships and causality graphs between agent steps.
|
|
482
|
+
|
|
483
|
+
Volidator parses W3C `traceparent` headers automatically when you pass a request object. Telemetry contexts emitted by LangChain, LlamaIndex, or standard instrumentations are inherited without manual piping:
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
await volidator.agent.toolCall({
|
|
487
|
+
req: request, // Automatically extracts traceId/spanId from incoming headers
|
|
488
|
+
toolName: "database_write",
|
|
489
|
+
success: true,
|
|
490
|
+
});
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## 16. Batch Ingestion (`logBatch`)
|
|
496
|
+
|
|
497
|
+
For high-throughput runtimes (like agents executing iterative reasoning loops or massive data imports), use `logBatch` to prepare and send multiple logs in a single HTTP request.
|
|
498
|
+
|
|
499
|
+
All cryptographic preparation (AES-256-GCM encryption, blind indexing) runs in parallel on the client before a single POST request is made.
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
const logs = [
|
|
503
|
+
{ actor: "agent-1", action: "thought", metadata: { step: 1 } },
|
|
504
|
+
{ actor: "agent-1", action: "tool_call", metadata: { toolName: "search" } },
|
|
505
|
+
{ actor: "agent-1", action: "thought", metadata: { step: 2 } },
|
|
506
|
+
];
|
|
507
|
+
|
|
508
|
+
const { accepted, rejected } = await volidator.logBatch(logs);
|
|
509
|
+
console.log(`Successfully ingested ${accepted} logs, failed to prepare ${rejected}`);
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
*Maximum batch size is 100 entries per request.*
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## License
|
|
517
|
+
|
|
518
|
+
MIT © Volidator Contributors
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
VolidatorClient
|
|
3
|
+
} from "./chunk-YI4DCCHT.js";
|
|
4
|
+
|
|
5
|
+
// src/middleware/next.ts
|
|
6
|
+
function withVolidator(client, handler) {
|
|
7
|
+
return async (req, ctx = {}, ...args) => {
|
|
8
|
+
const extractedContext = VolidatorClient.extractContext(req);
|
|
9
|
+
const scopedLog = async (payload) => {
|
|
10
|
+
return client.log({
|
|
11
|
+
...payload,
|
|
12
|
+
context: {
|
|
13
|
+
...extractedContext,
|
|
14
|
+
...payload.context,
|
|
15
|
+
location: {
|
|
16
|
+
...extractedContext.location,
|
|
17
|
+
...payload.context?.location || {}
|
|
18
|
+
},
|
|
19
|
+
device: {
|
|
20
|
+
...extractedContext.device,
|
|
21
|
+
...payload.context?.device || {}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
const withControl = (action, soc2Control, isoControl, payload) => scopedLog({
|
|
27
|
+
...payload,
|
|
28
|
+
action,
|
|
29
|
+
metadata: {
|
|
30
|
+
...payload.metadata,
|
|
31
|
+
soc2_control: soc2Control,
|
|
32
|
+
iso27001: isoControl
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
const scopedCompliance = {
|
|
36
|
+
accessRevoked: (p) => withControl("access.revoked", "CC6.1", "A.9.2.6", p),
|
|
37
|
+
accessGranted: (p) => withControl("access.granted", "CC6.1", "A.9.2.1", p),
|
|
38
|
+
dataExported: (p) => withControl("data.exported", "CC6.6", "A.12.4.1", p),
|
|
39
|
+
systemConfigChanged: (p) => withControl("system.config_changed", "CC6.2", "A.12.1.2", p),
|
|
40
|
+
mfaEnabled: (p) => withControl("mfa.enabled", "CC6.3", "A.9.4.2", p)
|
|
41
|
+
};
|
|
42
|
+
const newCtx = {
|
|
43
|
+
...ctx,
|
|
44
|
+
volidator: {
|
|
45
|
+
log: scopedLog,
|
|
46
|
+
compliance: scopedCompliance
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
return handler(req, newCtx, ...args);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export {
|
|
54
|
+
withVolidator
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=chunk-KEXIKZGN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/middleware/next.ts"],"sourcesContent":["import { VolidatorClient, LogPayload } from \"../index\";\n\n/**\n * Next.js App Router Middleware for Volidator.\n * Automatically extracts telemetry context (IP, User Agent, Location)\n * from the Next.js standard Request object and injects it into every\n * `volidator.log()` and `volidator.compliance.*()` call made inside\n * the wrapped handler.\n */\nexport function withVolidator<T extends Function>(client: VolidatorClient, handler: T) {\n return async (req: Request, ctx: any = {}, ...args: any[]) => {\n const extractedContext = VolidatorClient.extractContext(req);\n\n // ---------------------------------------------------------------------------\n // Scoped log — merges request-level IP/UA context into every log call.\n // ---------------------------------------------------------------------------\n const scopedLog = async (payload: LogPayload) => {\n return client.log({\n ...payload,\n context: {\n ...extractedContext,\n ...payload.context,\n location: {\n ...extractedContext.location,\n ...(payload.context?.location || {}),\n },\n device: {\n ...extractedContext.device,\n ...(payload.context?.device || {}),\n },\n },\n });\n };\n\n // ---------------------------------------------------------------------------\n // Scoped compliance — mirrors VolidatorCompliance but routes through\n // scopedLog so that IP/UA telemetry is preserved in compliance events.\n //\n // Previously this passed client.compliance directly, which called\n // client.log() without the request context — silently dropping telemetry\n // from every compliance audit event.\n // ---------------------------------------------------------------------------\n const withControl = (\n action: string,\n soc2Control: string,\n isoControl: string,\n payload: Omit<LogPayload, \"action\">\n ) =>\n scopedLog({\n ...payload,\n action,\n metadata: {\n ...payload.metadata,\n soc2_control: soc2Control,\n iso27001: isoControl,\n },\n });\n\n const scopedCompliance = {\n accessRevoked: (p: Omit<LogPayload, \"action\">) =>\n withControl(\"access.revoked\", \"CC6.1\", \"A.9.2.6\", p),\n accessGranted: (p: Omit<LogPayload, \"action\">) =>\n withControl(\"access.granted\", \"CC6.1\", \"A.9.2.1\", p),\n dataExported: (p: Omit<LogPayload, \"action\">) =>\n withControl(\"data.exported\", \"CC6.6\", \"A.12.4.1\", p),\n systemConfigChanged: (p: Omit<LogPayload, \"action\">) =>\n withControl(\"system.config_changed\", \"CC6.2\", \"A.12.1.2\", p),\n mfaEnabled: (p: Omit<LogPayload, \"action\">) =>\n withControl(\"mfa.enabled\", \"CC6.3\", \"A.9.4.2\", p),\n };\n\n const newCtx = {\n ...ctx,\n volidator: {\n log: scopedLog,\n compliance: scopedCompliance,\n },\n };\n\n return handler(req, newCtx, ...args);\n };\n}\n"],"mappings":";;;;;AASO,SAAS,cAAkC,QAAyB,SAAY;AACrF,SAAO,OAAO,KAAc,MAAW,CAAC,MAAM,SAAgB;AAC5D,UAAM,mBAAmB,gBAAgB,eAAe,GAAG;AAK3D,UAAM,YAAY,OAAO,YAAwB;AAC/C,aAAO,OAAO,IAAI;AAAA,QAChB,GAAG;AAAA,QACH,SAAS;AAAA,UACP,GAAG;AAAA,UACH,GAAG,QAAQ;AAAA,UACX,UAAU;AAAA,YACR,GAAG,iBAAiB;AAAA,YACpB,GAAI,QAAQ,SAAS,YAAY,CAAC;AAAA,UACpC;AAAA,UACA,QAAQ;AAAA,YACN,GAAG,iBAAiB;AAAA,YACpB,GAAI,QAAQ,SAAS,UAAU,CAAC;AAAA,UAClC;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAUA,UAAM,cAAc,CAClB,QACA,aACA,YACA,YAEA,UAAU;AAAA,MACR,GAAG;AAAA,MACH;AAAA,MACA,UAAU;AAAA,QACR,GAAG,QAAQ;AAAA,QACX,cAAc;AAAA,QACd,UAAU;AAAA,MACZ;AAAA,IACF,CAAC;AAEH,UAAM,mBAAmB;AAAA,MACvB,eAAe,CAAC,MACd,YAAY,kBAAkB,SAAS,WAAW,CAAC;AAAA,MACrD,eAAe,CAAC,MACd,YAAY,kBAAkB,SAAS,WAAW,CAAC;AAAA,MACrD,cAAc,CAAC,MACb,YAAY,iBAAiB,SAAS,YAAY,CAAC;AAAA,MACrD,qBAAqB,CAAC,MACpB,YAAY,yBAAyB,SAAS,YAAY,CAAC;AAAA,MAC7D,YAAY,CAAC,MACX,YAAY,eAAe,SAAS,WAAW,CAAC;AAAA,IACpD;AAEA,UAAM,SAAS;AAAA,MACb,GAAG;AAAA,MACH,WAAW;AAAA,QACT,KAAK;AAAA,QACL,YAAY;AAAA,MACd;AAAA,IACF;AAEA,WAAO,QAAQ,KAAK,QAAQ,GAAG,IAAI;AAAA,EACrC;AACF;","names":[]}
|