cloakllm 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ziv (Zivuch) Chen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,204 @@
1
+ # CloakLLM
2
+
3
+ **PII cloaking and tamper-evident audit logs for LLM API calls.**
4
+
5
+ CloakLLM intercepts your LLM API calls, detects and cloaks PII before it reaches the provider, and logs every event to a tamper-evident audit chain designed for EU AI Act Article 12 compliance.
6
+
7
+ > **Also available for Python:** `pip install cloakllm` — includes spaCy NER for name/org/location detection. See [CloakLLM Python](https://github.com/cloakllm/CloakLLM-PY).
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install cloakllm
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ### With OpenAI SDK (one line)
18
+
19
+ ```javascript
20
+ const cloakllm = require('cloakllm');
21
+ const OpenAI = require('openai');
22
+
23
+ const client = new OpenAI();
24
+ cloakllm.enable(client); // That's it. All calls are now cloaked.
25
+
26
+ const response = await client.chat.completions.create({
27
+ model: 'gpt-4o-mini',
28
+ messages: [{
29
+ role: 'user',
30
+ content: 'Write a reminder for sarah.j@techcorp.io about the Q3 audit'
31
+ }]
32
+ });
33
+ // Provider never saw "sarah.j@techcorp.io"
34
+ // Response has the real email restored automatically
35
+ ```
36
+
37
+ ### With Vercel AI SDK
38
+
39
+ ```javascript
40
+ const { createCloakLLMMiddleware } = require('cloakllm');
41
+ const { generateText, wrapLanguageModel } = require('ai');
42
+ const { openai } = require('@ai-sdk/openai');
43
+
44
+ const middleware = createCloakLLMMiddleware();
45
+ const model = wrapLanguageModel({ model: openai('gpt-4o'), middleware });
46
+
47
+ const { text } = await generateText({
48
+ model,
49
+ prompt: 'Write a reminder for sarah.j@techcorp.io about the Q3 audit'
50
+ });
51
+ // Provider never saw "sarah.j@techcorp.io"
52
+ // Response has the real email restored automatically
53
+ ```
54
+
55
+ Works with any AI SDK provider (OpenAI, Anthropic, Google, Mistral, etc.) and supports both `generateText` and `streamText`.
56
+
57
+ ### Standalone
58
+
59
+ ```javascript
60
+ const { Shield } = require('cloakllm');
61
+
62
+ const shield = new Shield();
63
+ const [sanitized, tokenMap] = shield.sanitize(
64
+ 'Send report to john@acme.com, SSN 123-45-6789'
65
+ );
66
+ // sanitized: "Send report to [EMAIL_0], SSN [SSN_0]"
67
+
68
+ // ... send sanitized text to any LLM ...
69
+
70
+ const restored = shield.desanitize(llmResponse, tokenMap);
71
+ // Original values restored
72
+ ```
73
+
74
+ ## What It Detects
75
+
76
+ | Category | Examples | Method |
77
+ |----------|----------|--------|
78
+ | `EMAIL` | `john@acme.com` | Regex |
79
+ | `SSN` | `123-45-6789` | Regex |
80
+ | `CREDIT_CARD` | `4111111111111111` | Regex |
81
+ | `PHONE` | `+1-555-0142` | Regex |
82
+ | `IP_ADDRESS` | `192.168.1.1` | Regex |
83
+ | `API_KEY` | `sk_live_abc123...` | Regex |
84
+ | `AWS_KEY` | `AKIAIOSFODNN7EXAMPLE` | Regex |
85
+ | `JWT` | `eyJhbG...` | Regex |
86
+ | `IBAN` | `DE89370400440532013000` | Regex |
87
+ | `PERSON` | John Smith | LLM (Local) |
88
+ | `ORG` | Acme Corp, Google | LLM (Local) |
89
+ | `GPE` | New York, Israel | LLM (Local) |
90
+ | `ADDRESS` | 742 Evergreen Terrace | LLM (Local) |
91
+ | `DATE_OF_BIRTH` | 1990-01-15 | LLM (Local) |
92
+ | `MEDICAL` | diabetes mellitus | LLM (Local) |
93
+ | `FINANCIAL` | account 4521-XXX | LLM (Local) |
94
+ | `NATIONAL_ID` | TZ 12345678 | LLM (Local) |
95
+ | `BIOMETRIC` | fingerprint hash | LLM (Local) |
96
+ | `USERNAME` | @johndoe42 | LLM (Local) |
97
+ | `PASSWORD` | P@ssw0rd123 | LLM (Local) |
98
+ | `VEHICLE` | plate ABC-1234 | LLM (Local) |
99
+
100
+ > **LLM categories** require opt-in (`llmDetection: true`) and a local [Ollama](https://ollama.com) instance. Data never leaves your machine.
101
+
102
+ ## How It Works
103
+
104
+ ```
105
+ Your app: "Email sarah.j@techcorp.io about Project Falcon"
106
+ Provider sees: "Email [EMAIL_0] about Project Falcon"
107
+ You receive: Original email restored in the response
108
+ ```
109
+
110
+ 1. **Detect** — Regex patterns find structured PII (emails, SSNs, credit cards, etc.)
111
+ 2. **Cloak** — Replace with deterministic tokens: `[EMAIL_0]`, `[SSN_0]`
112
+ 3. **Log** — Write to hash-chained audit trail (each entry includes previous entry's SHA-256 hash)
113
+ 4. **Uncloak** — Restore original values in the LLM response
114
+
115
+ ## Tamper-Evident Audit Chain
116
+
117
+ Every event is logged to JSONL files with hash chaining:
118
+
119
+ ```json
120
+ {
121
+ "seq": 42,
122
+ "event_type": "sanitize",
123
+ "entity_count": 3,
124
+ "categories": {"EMAIL": 1, "SSN": 1, "PHONE": 1},
125
+ "prompt_hash": "sha256:9f86d0...",
126
+ "prev_hash": "sha256:7c4d2e...",
127
+ "entry_hash": "sha256:b5e8f3..."
128
+ }
129
+ ```
130
+
131
+ Modify any entry and every subsequent hash breaks. Verify with:
132
+
133
+ ```bash
134
+ npx cloakllm verify ./cloakllm_audit/
135
+ ```
136
+
137
+ ## CLI
138
+
139
+ ```bash
140
+ # Scan text for PII
141
+ npx cloakllm scan "Email john@test.com, SSN 123-45-6789"
142
+
143
+ # Verify audit chain integrity
144
+ npx cloakllm verify ./cloakllm_audit/
145
+
146
+ # Show audit statistics
147
+ npx cloakllm stats ./cloakllm_audit/
148
+ ```
149
+
150
+ ## Configuration
151
+
152
+ ```javascript
153
+ const { Shield, ShieldConfig } = require('cloakllm');
154
+
155
+ const shield = new Shield(new ShieldConfig({
156
+ detectEmails: true, // default: true
157
+ detectPhones: true,
158
+ detectSsns: true,
159
+ detectCreditCards: true,
160
+ detectApiKeys: true,
161
+ detectIpAddresses: true,
162
+ detectIban: true,
163
+ logDir: './my-audit-logs', // default: ./cloakllm_audit
164
+ auditEnabled: true, // default: true
165
+ skipModels: ['ollama/'], // skip local models
166
+ customPatterns: [
167
+ { name: 'EMPLOYEE_ID', pattern: 'EMP-\\d{6}' }
168
+ ],
169
+
170
+ // LLM Detection (opt-in, requires Ollama)
171
+ llmDetection: true, // Enable LLM-based detection
172
+ llmModel: 'llama3.2', // Ollama model
173
+ llmOllamaUrl: 'http://localhost:11434', // Ollama endpoint
174
+ llmTimeout: 10000, // Timeout in ms
175
+ llmConfidence: 0.85, // Confidence score
176
+ }));
177
+ ```
178
+
179
+ ## EU AI Act Compliance
180
+
181
+ Article 12 of the EU AI Act requires tamper-evident audit logs for AI systems. Enforcement begins **August 2, 2026**. CloakLLM provides:
182
+
183
+ - **Hash-chained logs** — cryptographically linked, any modification breaks the chain
184
+ - **O(n) verification** — `cloakllm verify` audits the entire chain
185
+ - **No PII in logs** — only hashes and token counts are logged (original values never stored)
186
+ - **Event-level detail** — every sanitize/desanitize event is recorded
187
+
188
+ ## Roadmap
189
+
190
+ - [x] NER-based detection (names, orgs, locations) via local LLM
191
+ - [x] Local LLM detection (opt-in, via Ollama)
192
+ - [ ] Streaming response support
193
+ - [x] Vercel AI SDK middleware
194
+ - [ ] LangChain.js integration
195
+ - [ ] OpenTelemetry span emission
196
+ - [ ] RFC 3161 trusted timestamping
197
+
198
+ ## License
199
+
200
+ MIT — See [LICENSE](LICENSE).
201
+
202
+ ## See Also
203
+
204
+ - **[CloakLLM Python](https://github.com/cloakllm/CloakLLM-PY)** — Python version with spaCy NER + LiteLLM integration
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "cloakllm",
3
+ "version": "0.1.1",
4
+ "description": "PII cloaking and tamper-evident audit logs for LLM API calls. EU AI Act Article 12 compliance.",
5
+ "main": "src/index.js",
6
+ "types": "src/index.d.ts",
7
+ "bin": {
8
+ "cloakllm": "./src/cli.js"
9
+ },
10
+ "scripts": {
11
+ "test": "node --test test/*.js"
12
+ },
13
+ "keywords": [
14
+ "llm",
15
+ "privacy",
16
+ "pii",
17
+ "compliance",
18
+ "eu-ai-act",
19
+ "openai",
20
+ "vercel-ai-sdk",
21
+ "audit",
22
+ "security",
23
+ "gdpr",
24
+ "data-protection"
25
+ ],
26
+ "author": "Ziv (Zivuch) Chen",
27
+ "license": "MIT",
28
+ "homepage": "https://github.com/cloakllm/CloakLLM-JS",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/cloakllm/CloakLLM-JS"
32
+ },
33
+ "bugs": {
34
+ "url": "https://github.com/cloakllm/CloakLLM-JS/issues"
35
+ },
36
+ "engines": {
37
+ "node": ">=18.0.0"
38
+ },
39
+ "files": [
40
+ "src/",
41
+ "LICENSE",
42
+ "README.md"
43
+ ],
44
+ "peerDependencies": {
45
+ "openai": ">=4.0.0",
46
+ "ai": ">=4.0.0"
47
+ },
48
+ "peerDependenciesMeta": {
49
+ "openai": {
50
+ "optional": true
51
+ },
52
+ "ai": {
53
+ "optional": true
54
+ }
55
+ }
56
+ }
package/src/audit.js ADDED
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Tamper-Evident Audit Logger.
3
+ *
4
+ * Hash-chained append-only JSONL logs for EU AI Act Article 12 compliance.
5
+ * Each entry's SHA-256 hash includes the previous entry's hash.
6
+ * Any modification breaks the chain from that point forward.
7
+ */
8
+
9
+ const crypto = require('crypto');
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ const GENESIS_HASH = '0'.repeat(64);
14
+
15
+ class AuditLogger {
16
+ /**
17
+ * @param {import('./config').ShieldConfig} config
18
+ */
19
+ constructor(config) {
20
+ this.config = config;
21
+ this._seq = 0;
22
+ this._prevHash = GENESIS_HASH;
23
+ this._logDir = config.logDir;
24
+ this._initialized = false;
25
+ }
26
+
27
+ _ensureInit() {
28
+ if (this._initialized) return;
29
+
30
+ fs.mkdirSync(this._logDir, { recursive: true });
31
+
32
+ // Recover chain state from most recent log file
33
+ const logFiles = this._getLogFiles();
34
+ if (logFiles.length > 0) {
35
+ const lastFile = logFiles[logFiles.length - 1];
36
+ try {
37
+ const content = fs.readFileSync(lastFile, 'utf-8');
38
+ const lines = content.split('\n').filter(l => l.trim());
39
+ if (lines.length > 0) {
40
+ const lastEntry = JSON.parse(lines[lines.length - 1]);
41
+ this._seq = lastEntry.seq + 1;
42
+ this._prevHash = lastEntry.entry_hash;
43
+ }
44
+ } catch {
45
+ // Start fresh if corrupted
46
+ }
47
+ }
48
+
49
+ this._initialized = true;
50
+ }
51
+
52
+ _getLogFiles() {
53
+ if (!fs.existsSync(this._logDir)) return [];
54
+ return fs.readdirSync(this._logDir)
55
+ .filter(f => f.startsWith('audit_') && f.endsWith('.jsonl'))
56
+ .sort()
57
+ .map(f => path.join(this._logDir, f));
58
+ }
59
+
60
+ _getLogFile() {
61
+ const today = new Date().toISOString().split('T')[0];
62
+ return path.join(this._logDir, `audit_${today}.jsonl`);
63
+ }
64
+
65
+ /**
66
+ * Compute SHA-256 hash of entry data.
67
+ * @param {Object} data
68
+ * @returns {string}
69
+ */
70
+ static computeHash(data) {
71
+ // Deterministic serialization: recursively sort keys at all levels
72
+ const sorted = JSON.stringify(data, (_, v) => {
73
+ if (v && typeof v === 'object' && !Array.isArray(v)) {
74
+ return Object.keys(v).sort().reduce((o, k) => { o[k] = v[k]; return o; }, {});
75
+ }
76
+ return v;
77
+ });
78
+ return crypto.createHash('sha256').update(sorted).digest('hex');
79
+ }
80
+
81
+ /**
82
+ * Append a new entry to the audit log.
83
+ * @param {Object} options
84
+ * @returns {Object|null}
85
+ */
86
+ log({
87
+ eventType,
88
+ originalText = '',
89
+ sanitizedText = '',
90
+ model = null,
91
+ provider = null,
92
+ entityCount = 0,
93
+ categories = {},
94
+ tokensUsed = [],
95
+ latencyMs = 0,
96
+ metadata = {},
97
+ }) {
98
+ if (!this.config.auditEnabled) return null;
99
+
100
+ this._ensureInit();
101
+
102
+ const entryData = {
103
+ seq: this._seq,
104
+ event_id: crypto.randomUUID(),
105
+ timestamp: new Date().toISOString(),
106
+ event_type: eventType,
107
+ model,
108
+ provider,
109
+ entity_count: entityCount,
110
+ categories,
111
+ tokens_used: tokensUsed,
112
+ prompt_hash: originalText
113
+ ? crypto.createHash('sha256').update(originalText).digest('hex')
114
+ : '',
115
+ sanitized_hash: sanitizedText
116
+ ? crypto.createHash('sha256').update(sanitizedText).digest('hex')
117
+ : '',
118
+ latency_ms: Math.round(latencyMs * 100) / 100,
119
+ prev_hash: this._prevHash,
120
+ metadata,
121
+ };
122
+
123
+ const entryHash = AuditLogger.computeHash(entryData);
124
+ entryData.entry_hash = entryHash;
125
+
126
+ // Write to log file
127
+ const logFile = this._getLogFile();
128
+ fs.appendFileSync(logFile, JSON.stringify(entryData) + '\n');
129
+
130
+ // Update chain state
131
+ this._prevHash = entryHash;
132
+ this._seq += 1;
133
+
134
+ return entryData;
135
+ }
136
+
137
+ /**
138
+ * Verify the integrity of the entire audit chain.
139
+ * @param {string} [logFilePath] - Specific file, or all files in logDir
140
+ * @returns {{ valid: boolean, errors: string[] }}
141
+ */
142
+ verifyChain(logFilePath = null) {
143
+ const errors = [];
144
+ const files = logFilePath ? [logFilePath] : this._getLogFiles();
145
+
146
+ if (files.length === 0) return { valid: true, errors: [] };
147
+
148
+ let prevHash = GENESIS_HASH;
149
+
150
+ for (const fpath of files) {
151
+ const content = fs.readFileSync(fpath, 'utf-8');
152
+ const lines = content.split('\n').filter(l => l.trim());
153
+ const fname = path.basename(fpath);
154
+
155
+ for (let i = 0; i < lines.length; i++) {
156
+ let entry;
157
+ try {
158
+ entry = JSON.parse(lines[i]);
159
+ } catch {
160
+ errors.push(`${fname}:${i + 1} — Invalid JSON`);
161
+ continue;
162
+ }
163
+
164
+ // Check chain link
165
+ if (entry.prev_hash !== prevHash) {
166
+ errors.push(
167
+ `${fname}:${i + 1} seq=${entry.seq} — ` +
168
+ `Chain broken: expected prev_hash=${prevHash.slice(0, 16)}..., ` +
169
+ `got ${(entry.prev_hash || 'MISSING').slice(0, 16)}...`
170
+ );
171
+ }
172
+
173
+ // Recompute entry hash
174
+ const storedHash = entry.entry_hash;
175
+ delete entry.entry_hash;
176
+ const recomputed = AuditLogger.computeHash(entry);
177
+ if (storedHash !== recomputed) {
178
+ errors.push(
179
+ `${fname}:${i + 1} seq=${entry.seq} — ` +
180
+ `Entry tampered: stored_hash=${storedHash.slice(0, 16)}..., ` +
181
+ `recomputed=${recomputed.slice(0, 16)}...`
182
+ );
183
+ }
184
+
185
+ prevHash = storedHash;
186
+ }
187
+ }
188
+
189
+ return { valid: errors.length === 0, errors };
190
+ }
191
+
192
+ /**
193
+ * Get aggregate statistics from audit logs.
194
+ * @returns {Object}
195
+ */
196
+ getStats() {
197
+ this._ensureInit();
198
+ const stats = {
199
+ total_events: 0,
200
+ total_entities_detected: 0,
201
+ categories: {},
202
+ models_used: new Set(),
203
+ log_files: [],
204
+ };
205
+
206
+ for (const fpath of this._getLogFiles()) {
207
+ stats.log_files.push(path.basename(fpath));
208
+ const content = fs.readFileSync(fpath, 'utf-8');
209
+ for (const line of content.split('\n').filter(l => l.trim())) {
210
+ try {
211
+ const entry = JSON.parse(line);
212
+ stats.total_events += 1;
213
+ stats.total_entities_detected += entry.entity_count || 0;
214
+ for (const [cat, count] of Object.entries(entry.categories || {})) {
215
+ stats.categories[cat] = (stats.categories[cat] || 0) + count;
216
+ }
217
+ if (entry.model) stats.models_used.add(entry.model);
218
+ } catch { /* skip corrupt lines */ }
219
+ }
220
+ }
221
+
222
+ stats.models_used = [...stats.models_used];
223
+ return stats;
224
+ }
225
+ }
226
+
227
+ module.exports = { AuditLogger, GENESIS_HASH };
package/src/cli.js ADDED
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * CloakLLM CLI.
5
+ *
6
+ * Usage:
7
+ * cloakllm scan "Send email to john@acme.com, SSN 123-45-6789"
8
+ * cloakllm verify ./cloakllm_audit/
9
+ * cloakllm stats ./cloakllm_audit/
10
+ */
11
+
12
+ const { Shield } = require('./shield');
13
+ const { ShieldConfig } = require('./config');
14
+ const { AuditLogger } = require('./audit');
15
+
16
+ const args = process.argv.slice(2);
17
+ const command = args[0];
18
+
19
+ function cmdScan() {
20
+ const text = args.slice(1).join(' ');
21
+ if (!text) {
22
+ console.error('Usage: cloakllm scan "text to scan"');
23
+ process.exit(1);
24
+ }
25
+
26
+ const config = new ShieldConfig({ auditEnabled: false });
27
+ const shield = new Shield(config);
28
+
29
+ const analysis = shield.analyze(text);
30
+
31
+ if (analysis.entity_count === 0) {
32
+ console.log('✅ No sensitive entities detected.');
33
+ return;
34
+ }
35
+
36
+ console.log(`⚠️ Found ${analysis.entity_count} sensitive entities:\n`);
37
+
38
+ for (const ent of analysis.entities) {
39
+ console.log(` [${ent.category}] "${ent.text}"`);
40
+ console.log(` Position: ${ent.start}-${ent.end} | Confidence: ${Math.round(ent.confidence * 100)}% | Source: ${ent.source}`);
41
+ }
42
+
43
+ const [sanitized, tokenMap] = shield.sanitize(text);
44
+ console.log(`\n${'─'.repeat(60)}`);
45
+ console.log(`ORIGINAL: ${text}`);
46
+ console.log(`SANITIZED: ${sanitized}`);
47
+ console.log(`${'─'.repeat(60)}`);
48
+ console.log(`\nToken map (${tokenMap.entityCount} entities):`);
49
+ for (const [token, original] of tokenMap.reverse) {
50
+ console.log(` ${token} → "${original}"`);
51
+ }
52
+ }
53
+
54
+ function warnIfOutsideCwd(dirPath) {
55
+ const path = require('path');
56
+ const resolved = path.resolve(dirPath);
57
+ const cwd = process.cwd();
58
+ if (!resolved.startsWith(cwd + path.sep) && resolved !== cwd) {
59
+ console.warn(`Warning: Log directory '${resolved}' is outside the current working directory.`);
60
+ }
61
+ }
62
+
63
+ function cmdVerify() {
64
+ const logDir = args[1];
65
+ if (!logDir) {
66
+ console.error('Usage: cloakllm verify <log_dir>');
67
+ process.exit(1);
68
+ }
69
+
70
+ warnIfOutsideCwd(logDir);
71
+
72
+ const fs = require('fs');
73
+ if (!fs.existsSync(logDir)) {
74
+ console.error(`❌ Log directory not found: ${logDir}`);
75
+ process.exit(1);
76
+ }
77
+
78
+ const config = new ShieldConfig({ logDir });
79
+ const logger = new AuditLogger(config);
80
+
81
+ console.log(`Verifying audit chain in ${logDir}...`);
82
+ const { valid, errors } = logger.verifyChain();
83
+
84
+ if (valid) {
85
+ console.log('✅ Audit chain integrity verified — no tampering detected.');
86
+ } else {
87
+ console.error(`❌ CHAIN INTEGRITY FAILURE — ${errors.length} error(s):\n`);
88
+ for (const err of errors) {
89
+ console.error(` • ${err}`);
90
+ }
91
+ process.exit(1);
92
+ }
93
+ }
94
+
95
+ function cmdStats() {
96
+ const logDir = args[1];
97
+ if (!logDir) {
98
+ console.error('Usage: cloakllm stats <log_dir>');
99
+ process.exit(1);
100
+ }
101
+
102
+ warnIfOutsideCwd(logDir);
103
+
104
+ const config = new ShieldConfig({ logDir });
105
+ const logger = new AuditLogger(config);
106
+ console.log(JSON.stringify(logger.getStats(), null, 2));
107
+ }
108
+
109
+ switch (command) {
110
+ case 'scan':
111
+ cmdScan();
112
+ break;
113
+ case 'verify':
114
+ cmdVerify();
115
+ break;
116
+ case 'stats':
117
+ cmdStats();
118
+ break;
119
+ default:
120
+ console.log('CloakLLM — AI Compliance Middleware CLI\n');
121
+ console.log('Commands:');
122
+ console.log(' scan <text> Scan text for sensitive data');
123
+ console.log(' verify <dir> Verify audit log integrity');
124
+ console.log(' stats <dir> Show audit statistics');
125
+ break;
126
+ }
package/src/config.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * CloakLLM Configuration.
3
+ *
4
+ * All settings have sensible defaults. Override via constructor:
5
+ * const config = new ShieldConfig({ logDir: './my-audit-logs' });
6
+ * const shield = new Shield(config);
7
+ *
8
+ * Or via environment variables:
9
+ * CLOAKLLM_LOG_DIR=./my-audit-logs
10
+ */
11
+
12
+ class ShieldConfig {
13
+ constructor(options = {}) {
14
+ // --- Detection ---
15
+ this.detectEmails = options.detectEmails ?? true;
16
+ this.detectPhones = options.detectPhones ?? true;
17
+ this.detectSsns = options.detectSsns ?? true;
18
+ this.detectCreditCards = options.detectCreditCards ?? true;
19
+ this.detectApiKeys = options.detectApiKeys ?? true;
20
+ this.detectIpAddresses = options.detectIpAddresses ?? true;
21
+ this.detectIban = options.detectIban ?? true;
22
+ /** @type {Array<{name: string, pattern: string}>} */
23
+ this.customPatterns = options.customPatterns ?? [];
24
+
25
+ // --- LLM Detection (Pass 2: local LLM via Ollama) ---
26
+ this.llmDetection = options.llmDetection ??
27
+ (process.env.CLOAKLLM_LLM_DETECTION ?? 'false').toLowerCase() === 'true';
28
+ this.llmModel = options.llmModel ?? process.env.CLOAKLLM_LLM_MODEL ?? 'llama3.2';
29
+ this.llmOllamaUrl = options.llmOllamaUrl ?? process.env.CLOAKLLM_OLLAMA_URL ?? 'http://localhost:11434';
30
+ this.llmTimeout = options.llmTimeout ?? 10000;
31
+ this.llmConfidence = options.llmConfidence ?? 0.85;
32
+
33
+ // --- Tokenization ---
34
+ this.descriptiveTokens = options.descriptiveTokens ?? true;
35
+
36
+ // --- Audit Logging ---
37
+ this.auditEnabled = options.auditEnabled ?? true;
38
+ this.logDir = options.logDir ?? process.env.CLOAKLLM_LOG_DIR ?? './cloakllm_audit';
39
+ this.logOriginalValues = options.logOriginalValues ?? false;
40
+
41
+ // --- Middleware ---
42
+ this.autoMode = options.autoMode ?? true;
43
+ /** @type {string[]} Model prefixes to skip sanitization */
44
+ this.skipModels = options.skipModels ?? [];
45
+ }
46
+ }
47
+
48
+ module.exports = { ShieldConfig };