@intflows/genkit-guard 0.0.13 → 0.1.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 +337 -191
- package/dist/core/audit.d.ts +5 -0
- package/dist/core/audit.js +23 -0
- package/dist/core/decision-storage.d.ts +50 -0
- package/dist/core/decision-storage.js +84 -0
- package/dist/core/decision.d.ts +33 -0
- package/dist/core/decision.js +9 -0
- package/dist/guard.config.d.ts +10 -0
- package/dist/guard.config.js +12 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +11 -10
- package/dist/intent/intentAnalyzer.d.ts +1 -1
- package/dist/intent/intentAnalyzer.js +2 -2
- package/dist/middleware/middleware.d.ts +183 -7
- package/dist/middleware/middleware.js +96 -6
- package/dist/pii/detector.d.ts +7 -5
- package/dist/pii/detector.js +47 -17
- package/dist/util/fallback.d.ts +8 -0
- package/dist/util/fallback.js +32 -0
- package/package.json +7 -3
- package/scripts/publish-wiki.js +59 -0
- package/scripts/test-config.js +45 -0
- package/scripts/test-publish-wiki.js +30 -0
- package/scripts/test-release1.js +83 -0
- package/scripts/test-release2.js +208 -0
- package/scripts/test-types.ts +41 -0
package/README.md
CHANGED
|
@@ -1,292 +1,438 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Genkit Guard
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Keep Genkit agents on topic, mask personal data before model calls, and control which tools can run.
|
|
4
4
|
|
|
5
|
-
`@intflows/genkit-guard`
|
|
6
|
-
It adds **semantic intent validation**, **PII masking/unmasking**, and **prompt‑injection detection** with minimal configuration.
|
|
5
|
+
`@intflows/genkit-guard` adds prompt-injection checks, semantic intent scoring, reversible PII masking, and tool policies to your Genkit application. Guard models run locally through Transformers.js; your generative model can use the provider you choose.
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
[Try the Hugging Face Space](https://huggingface.co/spaces/intflows/genkit-guard) · [Documentation](https://github.com/IntFlows/genkit-guard/wiki) · [Examples](./example/src)
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
## Why use it?
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
Agents can access customer data and trigger real workflows. Genkit Guard gives you controls at the model and tool boundaries:
|
|
13
12
|
|
|
14
|
-
- **
|
|
15
|
-
|
|
13
|
+
- **Check requests:** block known injection patterns and reject prompts below your intent similarity threshold.
|
|
14
|
+
- **Reduce PII exposure:** mask detected personal data before model calls and restore tokens in responses and tool inputs.
|
|
15
|
+
- **Control tools:** allow, block, redact detected PII, or require application approval before execution.
|
|
16
|
+
- **Audit decisions:** emit versioned decision events without raw prompts, arguments, or PII values.
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
Detects emails, phone numbers, names, and AU‑specific identifiers.
|
|
19
|
-
Replaces PII with reversible tokens before sending to the LLM.
|
|
18
|
+
Detection is based on patterns and model predictions. It can miss attacks or PII and can reject valid requests; evaluate it on your application's inputs and keep authorization in your application.
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
Restores original PII in the model’s response, even inside structured JSON.
|
|
20
|
+
## Full setup guide
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
Blocks jailbreak attempts using pattern‑based heuristics.
|
|
22
|
+
The following setup targets **v0.1.0** and can be used after that version is published. The package declares Genkit **1.39.0** as its peer dependency.
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
The package uses local `all-MiniLM-L6-v2` and `openai/privacy-filter` Models, these Models are downloaded once and cached locally.
|
|
29
|
-
|
|
30
|
-
- **Drop‑in Genkit Middleware**
|
|
31
|
-
Works with `ai.generate`, `ai.generateStream`, and Genkit flows.
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
### 📦 Installation
|
|
24
|
+
### 1. Create the application and install dependencies
|
|
36
25
|
|
|
37
26
|
```bash
|
|
38
|
-
|
|
39
|
-
|
|
27
|
+
mkdir my-genkit-app
|
|
28
|
+
cd my-genkit-app
|
|
29
|
+
npm init -y
|
|
30
|
+
npm pkg set type=module
|
|
31
|
+
npm install genkit@1.39.0 @genkit-ai/google-genai@1.39.0 @intflows/genkit-guard@0.1.0
|
|
32
|
+
npm install -D typescript tsx @types/node
|
|
33
|
+
mkdir src
|
|
40
34
|
```
|
|
41
35
|
|
|
42
|
-
|
|
36
|
+
For an existing Genkit application, install the guard package and use your existing provider configuration.
|
|
43
37
|
|
|
44
|
-
Download
|
|
38
|
+
### 2. Download the models
|
|
39
|
+
|
|
40
|
+
Download MiniLM for intent analysis and OpenAI's `privacy-filter` for PII detection. Run this command once from your application's root directory:
|
|
45
41
|
|
|
46
42
|
```bash
|
|
47
|
-
|
|
43
|
+
# Download MiniLM + OpenAI/privacy-filter
|
|
48
44
|
node node_modules/@intflows/genkit-guard/scripts/download-model.js
|
|
49
45
|
```
|
|
50
46
|
|
|
51
|
-
Models are cached locally and reused across runs.
|
|
47
|
+
Models are cached locally in `./models` and reused across runs. This script downloads those two models; it does not read `guard.config.ts`. The configuration below selects `pii.mode: "classifier"` to use `privacy-filter`. Download size and memory use depend on the models selected.
|
|
52
48
|
|
|
53
|
-
|
|
49
|
+
### 3. Configure the guard
|
|
54
50
|
|
|
55
|
-
|
|
51
|
+
Create `src/guard.config.ts`:
|
|
56
52
|
|
|
57
|
-
|
|
53
|
+
```ts
|
|
54
|
+
import { defineGuardConfig } from "@intflows/genkit-guard";
|
|
55
|
+
|
|
56
|
+
export default defineGuardConfig({
|
|
57
|
+
models: { extractor: "Xenova/all-MiniLM-L6-v2" },
|
|
58
|
+
intent: {
|
|
59
|
+
semantic: {
|
|
60
|
+
threshold: 0.7,
|
|
61
|
+
intents: {
|
|
62
|
+
support: "Technical support for Azure Blob Storage, APIs and integrations"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
pii: {
|
|
67
|
+
mode: "classifier",
|
|
68
|
+
model: "openai/privacy-filter"
|
|
69
|
+
},
|
|
70
|
+
tools: {
|
|
71
|
+
defaultAction: "block",
|
|
72
|
+
rules: { searchDocs: "allow" }
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
```
|
|
58
76
|
|
|
59
|
-
|
|
60
|
-
# Install @intflows/genkit-guard
|
|
61
|
-
npm install @intflows/genkit-guard
|
|
77
|
+
A separate configuration file is optional in general, but required by the import in this example. Pass the same configuration to startup and runtime; there is no automatic file discovery. See [shared model configuration](https://github.com/IntFlows/genkit-guard/wiki/8.-Shared-Model-Configuration) for alternatives.
|
|
62
78
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
_This downloads the models to `./models`; the total size is approximately 1.5 GB._
|
|
79
|
+
The tool rules apply to tools you separately register and supply to Genkit; they do not create tools. This configuration permits `searchDocs` and blocks other tool names.
|
|
80
|
+
|
|
81
|
+
### 4. Create the application entry point
|
|
67
82
|
|
|
68
|
-
|
|
83
|
+
Create `src/index.ts`:
|
|
69
84
|
|
|
70
85
|
```ts
|
|
86
|
+
import { genkit } from "genkit";
|
|
87
|
+
import { googleAI } from "@genkit-ai/google-genai";
|
|
71
88
|
import { guard, initGuard } from "@intflows/genkit-guard";
|
|
89
|
+
import guardConfig from "./guard.config.js";
|
|
72
90
|
|
|
73
|
-
|
|
91
|
+
const modelName = process.env.GEMINI_MODEL;
|
|
92
|
+
if (!modelName) throw new Error("Set GEMINI_MODEL before running the example");
|
|
74
93
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
guard({
|
|
79
|
-
intent: {
|
|
80
|
-
mode: "semantic",
|
|
81
|
-
allowedIntent: "integration",
|
|
82
|
-
semantic: {
|
|
83
|
-
threshold: 0.7,
|
|
84
|
-
intents: {
|
|
85
|
-
integration: "Azure Blob, APIs, workflows"
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
pii: { reversible: true }
|
|
90
|
-
})
|
|
91
|
-
]
|
|
94
|
+
const ai = genkit({
|
|
95
|
+
plugins: [googleAI()],
|
|
96
|
+
model: googleAI.model(modelName)
|
|
92
97
|
});
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
**You Can also check the full step by step guide here:**
|
|
96
|
-
|
|
97
|
-
[Intflows Wiki](https://github.com/IntFlows/genkit-guard/wiki)
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
await initGuard(guardConfig);
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
const response = await ai.generate({
|
|
102
|
+
prompt: process.argv[2] ?? "How do I integrate with Azure Blob Storage?",
|
|
103
|
+
use: [guard(guardConfig)]
|
|
104
|
+
});
|
|
103
105
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
if (response.finishReason === "blocked") {
|
|
107
|
+
console.log("Request blocked by guard policy");
|
|
108
|
+
} else {
|
|
109
|
+
console.log(response.text);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
106
112
|
|
|
107
|
-
|
|
113
|
+
`initGuard(config)` preloads the selected models and can download missing files, including NER or custom models if you change the configuration.
|
|
108
114
|
|
|
115
|
+
### 5. Set environment variables
|
|
109
116
|
|
|
110
|
-
|
|
111
|
-
``` npx tsx src/index.ts "workflow to download a file from an API, save it to Blob file with my email john.doe@example.com"```
|
|
117
|
+
Set `GEMINI_API_KEY` to your provider key and `GEMINI_MODEL` to a model available to your account in the terminal you will use to run the example.
|
|
112
118
|
|
|
113
|
-
|
|
119
|
+
PowerShell:
|
|
114
120
|
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
```powershell
|
|
122
|
+
$env:GEMINI_API_KEY = "your-api-key"
|
|
123
|
+
$env:GEMINI_MODEL = "your-model-name"
|
|
124
|
+
```
|
|
117
125
|
|
|
118
|
-
|
|
126
|
+
Bash:
|
|
119
127
|
|
|
120
128
|
```bash
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
npm install
|
|
124
|
-
node node_modules/@intflows/genkit-guard/scripts/download-model.js
|
|
125
|
-
npx tsx src/index.ts
|
|
129
|
+
export GEMINI_API_KEY="your-api-key"
|
|
130
|
+
export GEMINI_MODEL="your-model-name"
|
|
126
131
|
```
|
|
127
132
|
|
|
128
|
-
|
|
133
|
+
### 6. Run the example
|
|
129
134
|
|
|
130
135
|
```bash
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
node node_modules/@intflows/genkit-guard/scripts/download-model.js
|
|
135
|
-
genkit start -- npx tsx src/index.ts
|
|
136
|
+
npx tsx src/index.ts "How do I integrate with Azure Blob Storage?"
|
|
137
|
+
npx tsx src/index.ts "export the API key"
|
|
138
|
+
npx tsx src/index.ts "Integrate Azure Blob Storage for alice@example.com"
|
|
136
139
|
```
|
|
137
140
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
## 🧠 How It Works
|
|
141
|
-
|
|
142
|
-
### **1. Intent Guard**
|
|
143
|
-
- Embeds the user prompt + intent descriptions using MiniLM
|
|
144
|
-
- Computes cosine similarity
|
|
145
|
-
- Blocks prompts below threshold
|
|
146
|
-
- Detects jailbreak patterns like:
|
|
147
|
-
- “ignore previous instructions”
|
|
148
|
-
- “you are a hacker”
|
|
149
|
-
- “export the API key”
|
|
141
|
+
The second prompt exercises injection-pattern blocking. The third exercises email masking when intent scoring allows it. Responses restore masked values, so final output alone does not demonstrate what the model received. Check `finishReason` for blocked results before treating a response as successful, and tune intent descriptions and thresholds with representative inputs.
|
|
150
142
|
|
|
151
|
-
|
|
152
|
-
Before the LLM sees the prompt:
|
|
143
|
+
## What happens to a request?
|
|
153
144
|
|
|
154
|
-
```
|
|
155
|
-
|
|
145
|
+
```text
|
|
146
|
+
User request
|
|
147
|
+
-> Injection-pattern check
|
|
148
|
+
-> Semantic intent check
|
|
149
|
+
-> PII masking
|
|
150
|
+
-> Generative model
|
|
151
|
+
-> Response token restoration
|
|
152
|
+
-> Tool policy check before any requested tool executes
|
|
156
153
|
```
|
|
157
154
|
|
|
158
|
-
|
|
155
|
+
A matching injection phrase such as `ignore previous instructions` blocks the model call. Intent checks compare the prompt with every description in `intent.semantic.intents`; only include categories you want to allow.
|
|
159
156
|
|
|
160
|
-
|
|
161
|
-
- Phone numbers
|
|
162
|
-
- AU identifiers (Medicare, TFN, ABN, etc.)
|
|
163
|
-
- PII detected by local Model (OpenAI/privacy-filter)
|
|
157
|
+
PII masking replaces detected values with namespaced tokens:
|
|
164
158
|
|
|
165
|
-
|
|
166
|
-
|
|
159
|
+
```text
|
|
160
|
+
Input: Email alice@example.com
|
|
161
|
+
Model receives: Email [[EMAIL_<namespace>_0]]
|
|
162
|
+
Restored: Email alice@example.com
|
|
163
|
+
```
|
|
167
164
|
|
|
168
|
-
|
|
169
|
-
After the LLM responds:
|
|
165
|
+
Restoration also works inside structured responses. Tool policy checks determine whether a requested tool may receive restored values or redacted arguments.
|
|
170
166
|
|
|
167
|
+
## Tool controls
|
|
168
|
+
|
|
169
|
+
Add exact tool names to the shared configuration:
|
|
170
|
+
```text
|
|
171
|
+
Input: Email alice@example.com
|
|
172
|
+
Model receives: Email [[EMAIL_<namespace>_0]]
|
|
173
|
+
Restored: Email alice@example.com
|
|
171
174
|
```
|
|
172
|
-
"Send a confirmation email to [[EMAIL_0]]" → "Send a confirmation email to john.doe@example.com"
|
|
173
|
-
```
|
|
174
|
-
---
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
Restoration also works inside structured responses. Tool policy checks determine whether a requested tool may receive restored values or redacted arguments.
|
|
177
|
+
|
|
178
|
+
## Tool controls
|
|
177
179
|
|
|
178
|
-
|
|
180
|
+
Add exact tool names to the shared configuration:
|
|
179
181
|
|
|
180
182
|
```ts
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
183
|
+
tools: {
|
|
184
|
+
defaultAction: "block",
|
|
185
|
+
rules: {
|
|
186
|
+
searchDocs: "allow",
|
|
187
|
+
summarizeTicket: "redact",
|
|
188
|
+
deleteTicket: "approval-required"
|
|
189
|
+
},
|
|
190
|
+
approve: async ({ toolName, input, context }) => {
|
|
191
|
+
// Connect a trusted approval service for this exact call and user.
|
|
192
|
+
// This example denies every approval request.
|
|
193
|
+
return false;
|
|
194
|
+
tools: {
|
|
195
|
+
defaultAction: "block",
|
|
196
|
+
rules: {
|
|
197
|
+
searchDocs: "allow",
|
|
198
|
+
summarizeTicket: "redact",
|
|
199
|
+
deleteTicket: "approval-required"
|
|
200
|
+
},
|
|
201
|
+
approve: async ({ toolName, input, context }) => {
|
|
202
|
+
// Connect a trusted approval service for this exact call and user.
|
|
203
|
+
// This example denies every approval request.
|
|
204
|
+
return false;
|
|
189
205
|
}
|
|
190
206
|
}
|
|
191
207
|
```
|
|
192
208
|
|
|
193
|
-
|
|
209
|
+
| Policy | Behavior |
|
|
210
|
+
| --- | --- |
|
|
211
|
+
| `allow` | Restore tokens, scan input, then execute |
|
|
212
|
+
| `block` | Stop before execution |
|
|
213
|
+
| `redact` | Replace detected PII in nested string arguments with `[REDACTED]`, then execute |
|
|
214
|
+
| `approval-required` | Execute only when the application's callback returns literal `true` |
|
|
194
215
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
216
|
+
Blocked, pending, or denied calls throw `GuardToolError`. Approval UI and durable approval storage belong to the application. Redaction may invalidate a tool's input schema, such as an email field; choose a policy appropriate to the tool.
|
|
217
|
+
|
|
218
|
+
Without tool policies, the default remains allow. With `tools` configured, `guard()` returns a native Genkit middleware reference. Existing configurations without `tools` retain the legacy callable form. Use `guardMiddleware(config)` for native tool hooks without explicit policies.
|
|
219
|
+
|
|
220
|
+
See [tool controls and logging](https://github.com/IntFlows/genkit-guard/wiki/9.-Tool-Controls-and-Logging) for the complete behavior.
|
|
221
|
+
|
|
222
|
+
## Models and PII storage
|
|
223
|
+
|
|
224
|
+
| Setting | Default |
|
|
225
|
+
| --- | --- |
|
|
226
|
+
| Intent model | `Xenova/all-MiniLM-L6-v2` |
|
|
227
|
+
| PII mode | `ner` |
|
|
228
|
+
| NER model | `Xenova/bert-base-NER` |
|
|
229
|
+
| Classifier model | `openai/privacy-filter` when classifier mode is selected |
|
|
230
|
+
| PII vault | In-memory storage |
|
|
231
|
+
|
|
232
|
+
The quick start explicitly selects classifier mode. Regex detection also runs for email, Australian phone and identifier patterns, and credit-card-like numbers.
|
|
233
|
+
|
|
234
|
+
Use `models.extractor` and `pii.model` to select compatible models, and `pii.labelMappings` to map fine-tuned labels to masking types. Classifier mode currently loads `q4` weights. Redis and custom vault adapters support external storage.
|
|
235
|
+
|
|
236
|
+
- [Shared model configuration](https://github.com/IntFlows/genkit-guard/wiki/8.-Shared-Model-Configuration)
|
|
237
|
+
- [PII labels, masking and vaults](https://github.com/IntFlows/genkit-guard/wiki/5.-PII-Guard)
|
|
238
|
+
|
|
239
|
+
## PII Vault Isolation and External Storage
|
|
240
|
+
|
|
241
|
+
Masked values are kept in a vault so they can be restored later. By default, the middleware uses process-local in-memory storage with generated vault scopes. Each tokenizer also generates an opaque namespace for its placeholders:
|
|
242
|
+
| Policy | Behavior |
|
|
243
|
+
| --- | --- |
|
|
244
|
+
| `allow` | Restore tokens, scan input, then execute |
|
|
245
|
+
| `block` | Stop before execution |
|
|
246
|
+
| `redact` | Replace detected PII in nested string arguments with `[REDACTED]`, then execute |
|
|
247
|
+
| `approval-required` | Execute only when the application's callback returns literal `true` |
|
|
248
|
+
|
|
249
|
+
Blocked, pending, or denied calls throw `GuardToolError`. Approval UI and durable approval storage belong to the application. Redaction may invalidate a tool's input schema, such as an email field; choose a policy appropriate to the tool.
|
|
250
|
+
|
|
251
|
+
Without tool policies, the default remains allow. With `tools` configured, `guard()` returns a native Genkit middleware reference. Existing configurations without `tools` retain the legacy callable form. Use `guardMiddleware(config)` for native tool hooks without explicit policies.
|
|
252
|
+
|
|
253
|
+
See [tool controls and logging](https://github.com/IntFlows/genkit-guard/wiki/9.-Tool-Controls-and-Logging) for the complete behavior.
|
|
254
|
+
|
|
255
|
+
## Models and PII storage
|
|
256
|
+
|
|
257
|
+
| Setting | Default |
|
|
258
|
+
| --- | --- |
|
|
259
|
+
| Intent model | `Xenova/all-MiniLM-L6-v2` |
|
|
260
|
+
| PII mode | `ner` |
|
|
261
|
+
| NER model | `Xenova/bert-base-NER` |
|
|
262
|
+
| Classifier model | `openai/privacy-filter` when classifier mode is selected |
|
|
263
|
+
| PII vault | In-memory storage |
|
|
264
|
+
|
|
265
|
+
The quick start explicitly selects classifier mode. Regex detection also runs for email, Australian phone and identifier patterns, and credit-card-like numbers.
|
|
266
|
+
|
|
267
|
+
Use `models.extractor` and `pii.model` to select compatible models, and `pii.labelMappings` to map fine-tuned labels to masking types. Classifier mode currently loads `q4` weights. Redis and custom vault adapters support external storage.
|
|
268
|
+
|
|
269
|
+
- [Shared model configuration](https://github.com/IntFlows/genkit-guard/wiki/8.-Shared-Model-Configuration)
|
|
270
|
+
- [PII labels, masking and vaults](https://github.com/IntFlows/genkit-guard/wiki/5.-PII-Guard)
|
|
271
|
+
|
|
272
|
+
## PII Vault Isolation and External Storage
|
|
273
|
+
|
|
274
|
+
Masked values are kept in a vault so they can be restored later. By default, the middleware uses process-local in-memory storage with generated vault scopes. Each tokenizer also generates an opaque namespace for its placeholders:
|
|
275
|
+
|
|
276
|
+
```text
|
|
277
|
+
alice@example.com -> [[EMAIL_<namespace>_0]]
|
|
278
|
+
```text
|
|
279
|
+
alice@example.com -> [[EMAIL_<namespace>_0]]
|
|
223
280
|
```
|
|
224
281
|
|
|
225
|
-
|
|
282
|
+
Namespaces prevent concurrent calls from creating identical placeholder names. Use `pii.vault.scopeId` to group vault entries by request, session, or another application scope. The scope ID is not exposed in the placeholder.
|
|
283
|
+
|
|
284
|
+
### Redis storage
|
|
285
|
+
|
|
286
|
+
Use Redis when vault entries need to survive application restarts or be available to multiple workers. Install the client separately:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
npm install redis
|
|
290
|
+
```
|
|
226
291
|
|
|
227
|
-
|
|
228
|
-
|
|
292
|
+
Extend your shared configuration during application startup:
|
|
293
|
+
Namespaces prevent concurrent calls from creating identical placeholder names. Use `pii.vault.scopeId` to group vault entries by request, session, or another application scope. The scope ID is not exposed in the placeholder.
|
|
294
|
+
|
|
295
|
+
### Redis storage
|
|
296
|
+
|
|
297
|
+
Use Redis when vault entries need to survive application restarts or be available to multiple workers. Install the client separately:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
npm install redis
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Extend your shared configuration during application startup:
|
|
229
304
|
|
|
230
305
|
```ts
|
|
231
306
|
import { createClient } from "redis";
|
|
232
|
-
import { guard, createRedisPiiVaultStorage } from "@intflows/genkit-guard";
|
|
233
|
-
|
|
234
|
-
|
|
307
|
+
import { guard, initGuard, createRedisPiiVaultStorage } from "@intflows/genkit-guard";
|
|
308
|
+
import guardConfig from "./guard.config.js";
|
|
309
|
+
import { guard, initGuard, createRedisPiiVaultStorage } from "@intflows/genkit-guard";
|
|
310
|
+
import guardConfig from "./guard.config.js";
|
|
311
|
+
|
|
312
|
+
const redis = createClient({ url: process.env.REDIS_URL ?? "redis://localhost:6379" });
|
|
313
|
+
redis.on("error", () => console.error("PII vault Redis connection error"));
|
|
314
|
+
const redis = createClient({ url: process.env.REDIS_URL ?? "redis://localhost:6379" });
|
|
315
|
+
redis.on("error", () => console.error("PII vault Redis connection error"));
|
|
235
316
|
await redis.connect();
|
|
236
317
|
|
|
237
|
-
|
|
318
|
+
const config = {
|
|
319
|
+
...guardConfig,
|
|
320
|
+
const config = {
|
|
321
|
+
...guardConfig,
|
|
238
322
|
pii: {
|
|
239
|
-
|
|
323
|
+
...guardConfig.pii,
|
|
324
|
+
...guardConfig.pii,
|
|
240
325
|
vault: {
|
|
241
326
|
storage: createRedisPiiVaultStorage(redis, {
|
|
242
327
|
keyPrefix: "my-app:pii",
|
|
243
328
|
ttlSeconds: 3600,
|
|
244
|
-
fallbackToMemory:
|
|
329
|
+
fallbackToMemory: false
|
|
245
330
|
}),
|
|
246
|
-
|
|
331
|
+
// Populate this from trusted application context.
|
|
332
|
+
// If absent, the middleware generates a new scope.
|
|
333
|
+
scopeId: (_req: unknown, ctx: any) => ctx?.context?.piiScopeId
|
|
247
334
|
}
|
|
248
335
|
}
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
await initGuard(config);
|
|
339
|
+
// Use guard(config) in your ai.generate({ use: [...] }) calls.
|
|
340
|
+
const middleware = guard(config);
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
`ttlSeconds` expires the scoped vault and shared token-index keys; writes refresh their expiry, so it is not a per-entry retention deadline. Without a TTL, Redis entries remain until removed externally. The default in-memory vault has no automatic expiry.
|
|
344
|
+
|
|
345
|
+
`fallbackToMemory: true` enables a process-local mirror with the same TTL behavior when Redis operations fail. It is disabled by default, so Redis errors propagate. The mirror is not shared across workers, does not survive restarts, and is not automatically replayed into Redis after recovery.
|
|
346
|
+
|
|
347
|
+
### Custom storage and isolation boundaries
|
|
348
|
+
|
|
349
|
+
Use `createPiiVaultStorage({ get, set, entries, getByToken })` to connect another database or store. `getByToken` is optional and enables recovery of opaque tokens across model/tool turns.
|
|
350
|
+
|
|
351
|
+
Scoped reads and writes use `scopeId`, but cross-turn recovery can look up tokens across scopes within the same backend. Scope IDs and opaque namespaces are not tenant authorization controls. For separate tenants, use appropriately isolated storage adapters or Redis prefixes, including separate token indexes, and enforce access in your application. Vault entries contain original PII values; the adapter does not encrypt those values itself.
|
|
352
|
+
|
|
353
|
+
See the [PII vault documentation](https://github.com/IntFlows/genkit-guard/wiki/5.-PII-Guard) and [Redis integration example](./example/src/index.ts).
|
|
354
|
+
|
|
355
|
+
## Decision logging
|
|
356
|
+
|
|
357
|
+
Console logging emits structured JSON. In v0.1.0, attach a persistent store to your shared configuration:
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
import { defineGuardConfig, createJsonlDecisionStore } from "@intflows/genkit-guard";
|
|
361
|
+
|
|
362
|
+
const decisionStore = createJsonlDecisionStore("./logs/guard-decisions.jsonl");
|
|
363
|
+
const config = defineGuardConfig({
|
|
364
|
+
// Include your intent, PII and tool policies here.
|
|
365
|
+
policyVersion: "support-v2",
|
|
366
|
+
logging: {
|
|
367
|
+
enabled: false, // Disable console output; persistence still runs.
|
|
368
|
+
store: decisionStore
|
|
369
|
+
}
|
|
249
370
|
});
|
|
250
371
|
```
|
|
251
372
|
|
|
252
|
-
`
|
|
253
|
-
`fallbackToMemory` is enabled, successful writes are also mirrored in process memory and Redis
|
|
254
|
-
operation failures fall back to that mirror. The fallback is disabled by default, is local to one
|
|
255
|
-
process, and is not a replacement for Redis persistence or multi-worker availability. Its in-memory
|
|
256
|
-
entries observe the same TTL. Redis errors continue to propagate when fallback is disabled.
|
|
373
|
+
Pass `config` to `initGuard()` and `guard()`. Each append is validated, serialized and flushed before it resolves. Use `await decisionStore.read()` to load the records, including after restarting the application. Unknown fields are stripped before writing.
|
|
257
374
|
|
|
258
|
-
|
|
375
|
+
Events include schema version, decision ID, timestamp, guard, policy version, action, reason code, latency, and intent similarity where applicable. They exclude raw prompts, arguments, classifier output and error messages. Use non-sensitive policy identifiers.
|
|
259
376
|
|
|
260
|
-
|
|
377
|
+
`logging.onDecision` remains available. Store writes happen first, then the callback; both are awaited regardless of console settings. Failure stops the current operation. There are no automatic delivery retries, and a successful write is not rolled back if the callback later fails. Decisions describe checks, not confirmation of downstream execution.
|
|
261
378
|
|
|
262
|
-
|
|
263
|
-

|
|
379
|
+
The JSONL helper supports concurrent callers in one process, including separate instances using the same resolved path. Use separate files per worker or a shared backend through `createGuardDecisionStore({ append })` for multiple processes. File rotation, retention, encryption and crash recovery are application responsibilities; incomplete log tails cause reads and further appends to fail rather than silently discarding records. `read()` loads the full file into memory.
|
|
264
380
|
|
|
265
|
-
|
|
381
|
+
## Fallback guard models
|
|
266
382
|
|
|
267
|
-
|
|
383
|
+
Fallbacks are optional and apply to local intent and PII models, not your application's generative model:
|
|
268
384
|
|
|
269
|
-
|
|
385
|
+
```ts
|
|
386
|
+
models: {
|
|
387
|
+
extractor: "Xenova/all-MiniLM-L6-v2",
|
|
388
|
+
extractorFallback: "your-org/compatible-embedding-model"
|
|
389
|
+
},
|
|
390
|
+
pii: {
|
|
391
|
+
mode: "classifier",
|
|
392
|
+
model: "openai/privacy-filter",
|
|
393
|
+
fallback: {
|
|
394
|
+
model: "Xenova/bert-base-NER",
|
|
395
|
+
mode: "ner",
|
|
396
|
+
labelMappings: { PER: "NAME" }
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
```
|
|
270
400
|
|
|
271
|
-
|
|
272
|
-
- PII protection
|
|
273
|
-
- jailbreak resistance
|
|
274
|
-
- predictable behavior
|
|
401
|
+
Replace the custom embedding identifier with a model you have prepared. A fallback runs once when the primary model fails to load or execute. An intent rejection or empty PII result does not trigger fallback. The primary is tried again on the next operation; there is no timeout, circuit breaker or automatic switch for future requests.
|
|
275
402
|
|
|
276
|
-
|
|
403
|
+
PII fallback mode defaults to the primary mode. Its label mappings are independent of the primary mappings. Both models must support the relevant Transformers.js task; classifier mode requires compatible `q4` weights. NER and privacy-filter have different detection coverage, so evaluate the fallback on your own inputs before enabling it.
|
|
277
404
|
|
|
278
|
-
|
|
405
|
+
Successful recovery emits `MODEL_FALLBACK_USED`; normal guard checks still determine whether the request proceeds. If both models fail, `GuardModelError` with code `MODEL_UNAVAILABLE` stops the operation. There is no regex-only bypass. The same policy applies during `initGuard()`, model-request checks and tool PII scans. A failure after a tool has executed cannot undo that execution.
|
|
279
406
|
|
|
280
|
-
|
|
407
|
+
Without fallback configuration, existing model selection and error propagation remain unchanged. See [persistent decisions and fallback models](https://github.com/IntFlows/genkit-guard/wiki/10.-Decision-Storage-and-Model-Fallback) for the full contract.
|
|
408
|
+
|
|
409
|
+
## Try it and explore the examples
|
|
281
410
|
|
|
282
|
-
|
|
411
|
+
[Open the Hugging Face Space](https://huggingface.co/spaces/intflows/genkit-guard) to explore the demo. Use synthetic inputs when trying a hosted demo.
|
|
283
412
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
413
|
+
- [Integration flow](./example/src/index.ts): Azure Blob workflow with Redis-backed PII storage.
|
|
414
|
+
- [Tool controls](./example/src/tool-controls.ts): optional standalone demo of redaction and approval-required policies. This file is not required to use the package.
|
|
415
|
+
|
|
416
|
+
The tool-controls demo uses the local build. Run `npm install` and `npm run build` in the repository root, then `npm install` in `example`. Set `GEMINI_API_KEY` and `GEMINI_MODEL` for your provider account, and run:
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
# From example/
|
|
420
|
+
npx tsx src/tool-controls.ts
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
## Roadmap
|
|
424
|
+
|
|
425
|
+
- **v0.0.14:** shared model configuration, custom PII labels, tool policies and versioned decision events.
|
|
426
|
+
- **v0.1.0 (in development):** persistent decision logging and explicit fallback-model behavior.
|
|
427
|
+
- **Later:** SQLite vault, memory compaction integration, compatibility hardening and stable v1.
|
|
428
|
+
|
|
429
|
+
## Contributing
|
|
287
430
|
|
|
288
|
-
|
|
431
|
+
Issues, pull requests, model evaluations and security reviews are welcome. Run `npm test` for the deterministic suite; real Redis integration is tested separately with `npm run test:redis`.
|
|
432
|
+
Issues, pull requests, model evaluations and security reviews are welcome. Run `npm test` for the deterministic suite; real Redis integration is tested separately with `npm run test:redis`.
|
|
289
433
|
|
|
290
|
-
|
|
434
|
+
## License
|
|
435
|
+
## License
|
|
291
436
|
|
|
292
|
-
Apache
|
|
437
|
+
[Apache-2.0](./LICENSE)
|
|
438
|
+
[Apache-2.0](./LICENSE)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { GuardConfig } from '../middleware/middleware.js';
|
|
2
|
+
import type { GuardDecision } from './decision.js';
|
|
3
|
+
export declare function publishDecision(config: GuardConfig | undefined, start: number, fields: Pick<GuardDecision, 'guard' | 'action' | 'reasonCode'> & {
|
|
4
|
+
confidence?: number;
|
|
5
|
+
}): Promise<GuardDecision>;
|