code-airlock 0.1.0__tar.gz → 1.3.0__tar.gz

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.
Files changed (31) hide show
  1. code_airlock-1.3.0/LICENSE +21 -0
  2. code_airlock-1.3.0/PKG-INFO +395 -0
  3. code_airlock-1.3.0/README.md +356 -0
  4. code_airlock-1.3.0/pyproject.toml +56 -0
  5. {code_airlock-0.1.0 → code_airlock-1.3.0}/setup.cfg +4 -4
  6. code_airlock-1.3.0/src/code_airlock/__init__.py +7 -0
  7. code_airlock-1.3.0/src/code_airlock/__main__.py +127 -0
  8. code_airlock-1.3.0/src/code_airlock/config.py +39 -0
  9. {code_airlock-0.1.0 → code_airlock-1.3.0/src/code_airlock}/engine.py +219 -183
  10. code_airlock-1.3.0/src/code_airlock/executor.py +87 -0
  11. code_airlock-1.3.0/src/code_airlock/mcp_server.py +124 -0
  12. {code_airlock-0.1.0 → code_airlock-1.3.0/src/code_airlock}/proxy.py +13 -3
  13. {code_airlock-0.1.0 → code_airlock-1.3.0/src/code_airlock}/sanitizer.py +27 -7
  14. code_airlock-1.3.0/src/code_airlock/scanner.py +76 -0
  15. code_airlock-1.3.0/src/code_airlock.egg-info/PKG-INFO +395 -0
  16. code_airlock-1.3.0/src/code_airlock.egg-info/SOURCES.txt +20 -0
  17. code_airlock-1.3.0/src/code_airlock.egg-info/entry_points.txt +4 -0
  18. {code_airlock-0.1.0 → code_airlock-1.3.0/src}/code_airlock.egg-info/requires.txt +8 -0
  19. code_airlock-1.3.0/src/code_airlock.egg-info/top_level.txt +1 -0
  20. code_airlock-1.3.0/tests/test_mcp.py +181 -0
  21. code_airlock-1.3.0/tests/test_proxy.py +84 -0
  22. code_airlock-0.1.0/PKG-INFO +0 -208
  23. code_airlock-0.1.0/README.md +0 -192
  24. code_airlock-0.1.0/benchmark.py +0 -132
  25. code_airlock-0.1.0/code_airlock.egg-info/PKG-INFO +0 -208
  26. code_airlock-0.1.0/code_airlock.egg-info/SOURCES.txt +0 -13
  27. code_airlock-0.1.0/code_airlock.egg-info/entry_points.txt +0 -2
  28. code_airlock-0.1.0/code_airlock.egg-info/top_level.txt +0 -5
  29. code_airlock-0.1.0/mcp_server.py +0 -71
  30. code_airlock-0.1.0/pyproject.toml +0 -27
  31. {code_airlock-0.1.0 → code_airlock-1.3.0/src}/code_airlock.egg-info/dependency_links.txt +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sarthak Maurya
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.
@@ -0,0 +1,395 @@
1
+ Metadata-Version: 2.4
2
+ Name: code-airlock
3
+ Version: 1.3.0
4
+ Summary: Low-latency OpenAI-compatible PII redaction gateway and FastMCP server for coding agents
5
+ Author: Sarthak Maurya
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/sarthexe/code-airlock
8
+ Project-URL: Repository, https://github.com/sarthexe/code-airlock
9
+ Project-URL: Issues, https://github.com/sarthexe/code-airlock/issues
10
+ Project-URL: Dataset, https://huggingface.co/datasets/sarthax/pii-airlock-bench
11
+ Keywords: pii,privacy,secrets,dlp,mcp,llm,security
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Security
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: gliner>=0.2.13
25
+ Requires-Dist: torch>=2.0.0
26
+ Requires-Dist: numpy>=1.24.0
27
+ Requires-Dist: mcp<2,>=1.0
28
+ Requires-Dist: fastapi>=0.110
29
+ Requires-Dist: uvicorn>=0.27
30
+ Requires-Dist: httpx>=0.27
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=8.0; extra == "dev"
33
+ Requires-Dist: build>=1.2; extra == "dev"
34
+ Requires-Dist: twine>=5.0; extra == "dev"
35
+ Provides-Extra: benchmark
36
+ Requires-Dist: datasets>=2.18; extra == "benchmark"
37
+ Requires-Dist: huggingface_hub>=0.23; extra == "benchmark"
38
+ Dynamic: license-file
39
+
40
+ # 🛡️ Code-Airlock
41
+
42
+ **Low-Latency AI Privacy, Secret/PII Redaction & Streaming Rehydration Gateway (`OpenAI Proxy` + `FastMCP Server`)**
43
+
44
+ `code-airlock` is a zero-trust privacy layer for LLMs and agentic coding harnesses. It intercepts sensitive credentials, financial identifiers, and personal data before they leave your machine, replaces them with deterministic session tokens (`<API_KEY_1>`, `<PERSON_1>`), and seamlessly rehydrates them back into real-time Server-Sent Events (SSE) token streams or local file writes.
45
+
46
+ * **Dual Deployment Modes:** Run as a drop-in **OpenAI-compatible `/v1/chat/completions` reverse proxy** or as a **Model Context Protocol (MCP) server** (`stdio`) for OpenCode, Cursor, and Claude Code.
47
+ * **2-Stage Hybrid Cascade:** Combines sub-millisecond deterministic regex + mathematical checksums (**Luhn, Verhoeff $D_5$, ISO 7064 MOD-97**) with contextual neural NER (`urchade/gliner_small-v2.1`) and a curated developer allowlist.
48
+ * **Deterministic Secret Coverage:** Stage 1 also captures inline connection-string passwords and `PASSWORD` / `SECRET` / `ACCESS_TOKEN` / `API_KEY` assignments, excluding placeholders, booleans, function calls, and existing vault tokens.
49
+ * **Git & CI Guardrails:** `code-airlock scan --staged` catches secrets in staged files at sub-millisecond Stage 1 speed, masks every value in output, and installs a `.git/hooks/pre-commit` gate via `init --pre-commit`.
50
+ * **Safe Agent Execution:** `airlock_exec` injects real `.env` values into a guarded subprocess (`shell=False`) and scrubs both stdout and stderr before returning them to the model.
51
+ * **Project-Aware Rules:** `.airlock.toml` adds team allowlist terms and custom Stage-1 regexes such as `EMPLOYEE_ID = 'EMP-[0-9]{6}'`.
52
+ * **Zero-Delay Streaming Rehydration:** Stateful sliding-window SSE parser reassembles split tokens across arbitrary 2-byte network chunks without delaying code comparison or bitwise operators (`<`, `<<`, `template <typename T>`).
53
+ * **Minimalist Systems Footprint:** ~800 lines of core runtime code (`engine.py`, `sanitizer.py`, `proxy.py`, `mcp_server.py`, `scanner.py`, `config.py`, `executor.py`) plus a deterministic 2,000-row benchmark harness.
54
+
55
+ ---
56
+
57
+ ## 🏗️ System Architecture
58
+
59
+ ```mermaid
60
+ flowchart TB
61
+ classDef client fill:#1e293b,stroke:#64748b,stroke-width:2px,color:#f8fafc
62
+ classDef stage1 fill:#064e3b,stroke:#10b981,stroke-width:2px,color:#ecfdf5
63
+ classDef stage2 fill:#1e3a8a,stroke:#3b82f6,stroke-width:2px,color:#eff6ff
64
+ classDef merger fill:#312e81,stroke:#8b5cf6,stroke-width:2px,color:#f5f3ff
65
+ classDef vault fill:#7c2d12,stroke:#f97316,stroke-width:2px,color:#fff7ed
66
+ classDef stream fill:#4c1d95,stroke:#a855f7,stroke-width:2px,color:#faf5ff
67
+
68
+ Client(["🖥️ Client SDK / Agent Harness<br/>(OpenAI SDK, OpenCode, Cursor, Claude Code)"]):::client
69
+
70
+ subgraph Airlock["🛡️ CODE-AIRLOCK (proxy.py / mcp_server.py)"]
71
+ direction TB
72
+ subgraph Cascade["engine.py — 2-Stage Hybrid Cascade"]
73
+ S1["⚡ Stage 1: Deterministic Fast-Pass (~0.44ms)<br/>• Regex: API_KEY, AUTH_TOKEN, EMAIL, PHONE, IPv4<br/>• Checksums: Luhn (Card), Verhoeff (ID), MOD-97 (IBAN)"]:::stage1
74
+ S2["🧠 Stage 2: Guarded Neural NER (GLiNER-small)<br/>• Entities: PERSON, ORGANIZATION, LOCATION<br/>• Guards: 93-term TECH_ALLOWLIST + Code/Pronoun Filter"]:::stage2
75
+ Merger{"⚖️ Span Conflict Resolver<br/>Stage 1 Overrides Stage 2<br/>Longest Span Wins"}:::merger
76
+ S1 & S2 --> Merger
77
+ end
78
+
79
+ Vault[("🗄️ Ephemeral Session Vault<br/>sanitizer.py (VaultSession)<br/>Right-to-Left Tagging")]:::vault
80
+ Rehydrator["🪟 StreamRehydrator<br/>Stateful Sliding-Window SSE & File Parser"]:::stream
81
+
82
+ Merger --> Vault
83
+ Vault -.->|"O(1) Token Lookup"| Rehydrator
84
+ end
85
+
86
+ LLM(["☁️ Upstream LLM Provider<br/>*Sees Only &lt;ENTITY_n&gt; Tokens*"]):::client
87
+
88
+ Client -->|"1. Raw Prompt / File Read"| Cascade
89
+ Vault -->|"2. Sanitized Payload"| LLM
90
+ LLM -->|"3. Fragmented SSE Stream / Tool Patch"| Rehydrator
91
+ Rehydrator -->|"4. Restored Plaintext"| Client
92
+ ```
93
+
94
+ ---
95
+
96
+ ## 📊 Gold-Set Benchmark (`benchmarks/gold_set.jsonl`)
97
+
98
+ Evaluated on **285 developer prompts** (sampled from `CodeAlpaca_20K` and `OpenCodeInstruct` with injected `.env` blocks, ticket escalations, billing metadata, and C++ template syntax) totaling **744 ground-truth PII spans** across 11 categories:
99
+
100
+ | Metric / Entity Category | Strict (P / R / F1) | Partial (P / R / F1) | Latency / Verification |
101
+ | :--- | :---: | :---: | :--- |
102
+ | **Overall Benchmark (744 Spans)** | **96.5% / 99.1% / 97.7%** | **97.0% / 99.6% / 98.3%** | **285 / 285 SSE Round-Trip Pass** |
103
+ | **Stage 1 Latency (Deterministic)** | — | — | **0.44 ms (p50)** / 0.78 ms (p95) |
104
+ | **Total Cascade Latency (CPU/GPU)** | — | — | **14.6–42.5 ms (p50)** |
105
+ | `API_KEY` *(OpenAI, AWS, GitHub, Slack, Anthropic)* | 100 / 100 / **100%** | 100 / 100 / **100%** | Prefix + length/charset bounds |
106
+ | `AUTH_TOKEN` *(JWT, Bearer, PEM Private Keys)* | 100 / 100 / **100%** | 100 / 100 / **100%** | 3-part Base64URL + PEM headers |
107
+ | `CREDIT_CARD` *(13–19 digits)* | 100 / 100 / **100%** | 100 / 100 / **100%** | Pure-Python **Luhn Checksum** |
108
+ | `GOVT_ID` *(SSN, PAN, 12-Digit National ID)* | 100 / 100 / **100%** | 100 / 100 / **100%** | **Verhoeff $D_5$ Checksum** |
109
+ | `IBAN_CRYPTO` *(IBAN, ETH `0x...`)* | 100 / 100 / **100%** | 100 / 100 / **100%** | **ISO 7064 MOD-97** Checksum |
110
+ | `EMAIL` & `PHONE` | 100 / 100 / **100%** | 100 / 100 / **100%** | Negative lookbehind for order IDs |
111
+ | `IP_ADDRESS` *(IPv4)* | 94 / 100 / **97%** | 94 / 100 / **97%** | Octet range `0..255` (rejects `.300`) |
112
+ | `PERSON` | 99 / 99 / **99%** | 99 / 99 / **99%** | Pronoun + code-identifier stopword guard |
113
+ | `ORGANIZATION` | 89 / 95 / **92%** | 91 / 97 / **94%** | Filtered via 93-term `TECH_ALLOWLIST` |
114
+ | `LOCATION` | 88 / 98 / **93%** | 90 / 100 / **95%** | Adjacent comma-span coalescing |
115
+
116
+ * **Hard-Negative Suite:** **61/61 Passed** (100% rejection of non-Luhn 16-digit order IDs, Verhoeff-invalid 12-digit numbers, MOD-97-invalid IBANs, and `v192.168.1.300` version strings).
117
+ * **Tech Allowlist Check:** **30/30 Passed** (`FastAPI`, `Docker`, `Kubernetes`, `Redis`, `Postgres`, `AWS`, `Heroku`, etc. produce zero false positives).
118
+
119
+ ---
120
+
121
+ ## 🧪 Unified 2,000-Row Benchmark Matrix
122
+
123
+ `benchmarks/build_benchmarks.py` deterministically generates four stratified splits into the git-ignored `benchmarks/` directory (`seed=42`). `benchmarks/benchmark.py` evaluates them and writes [`benchmarks/BENCHMARK_RESULTS.md`](benchmarks/BENCHMARK_RESULTS.md).
124
+
125
+ | Split | Rows | Positive | Clean / Hard-neg | Ground-truth spans |
126
+ | :--- | ---: | ---: | ---: | ---: |
127
+ | `dev_prompts` | 1,000 | 600 | 300 / 100 | 2,400 |
128
+ | `git_leaks` | 500 | 350 | 0 / 150 | 1,400 |
129
+ | `mcp_configs` | 250 | 200 | 50 / 0 | 800 |
130
+ | `adversarial_sse` | 250 | 250 | 0 / 0 | 1,217 |
131
+ | **Total** | **2,000** | **1,400** | **350 / 250** | **5,817** |
132
+
133
+ Reference result (strict exact-span matching, `code-airlock` v1.3.0):
134
+
135
+ | Split | Strict P / R / F1 | Hard-neg | Clean 0-FP | SSE 1/2/7-byte |
136
+ | :--- | :---: | :---: | :---: | :---: |
137
+ | `dev_prompts` | 100% / 100% / 100% | 100/100 | 300/300 | 1000/1000 |
138
+ | `git_leaks` | 100% / 100% / 100% | 150/150 | — | 500/500 |
139
+ | `mcp_configs` | 100% / 100% / 100% | — | 50/50 | 250/250 |
140
+ | `adversarial_sse` | 100% / 100% / 100% | — | — | 250/250 |
141
+ | **Combined** | **100% / 100% / 100%** | **250/250** | **350/350** | **2000/2000** |
142
+
143
+ - Latency: **0.232 ms** p50 / 0.428 ms p95 Stage 1; **32.354 ms** p50 / 43.966 ms p95 Stage 2; **32.704 ms** p50 / 44.112 ms p95 total cascade.
144
+ - Per-entity support: `API_KEY` 756, `AUTH_TOKEN` 467, `CREDIT_CARD` 440, `EMAIL` 754, `PHONE` 465, `IP_ADDRESS` 442, `GOVT_ID` 465, `IBAN_CRYPTO` 465, `PERSON` 729, `ORGANIZATION` 417, `LOCATION` 417. Every entity is at **100% P/R/F1**.
145
+ - Invariants: MCP JSON parse **100/100**, JSON round-trip **100/100**, adversarial SSE **2,000/2,000**, hard negatives **250/250**, clean rows **350/350 with zero false positives**.
146
+
147
+ These are **synthetic, template-composed fixtures**, so the perfect headline is a regression/invariant result, not a claim of real-world accuracy. The older real-world suites (`benchmarks/gold_set.jsonl`: 96.7/99.1/97.9; Gitleaks artifacts: 100/88.5/93.9) remain the honest field tests.
148
+
149
+ ### 🤗 Published Dataset
150
+
151
+ One command publishes all four splits and the auto-generated card:
152
+
153
+ ```bash
154
+ python benchmarks/export_hf.py --repo-id sarthexe/pii-airlock-bench
155
+ ```
156
+
157
+ Dataset: [**sarthax/pii-airlock-bench**](https://huggingface.co/datasets/sarthax/pii-airlock-bench) (the token owner namespace is resolved automatically).
158
+
159
+ ```python
160
+ from datasets import load_dataset
161
+ ds = load_dataset("sarthax/pii-airlock-bench")
162
+ ```
163
+
164
+ ---
165
+
166
+ ## 🚀 Quickstart & MCP Setup
167
+
168
+ ### Option 1: Zero-Install via `uvx` (Recommended)
169
+
170
+ No cloning, manual virtual environments, or path debugging. Anyone with Astral's [`uv`](https://github.com/astral-sh/uv) can run `code-airlock` directly from PyPI:
171
+
172
+ #### 🤖 Claude Code (CLI)
173
+ ```bash
174
+ claude mcp add code-airlock -- uvx code-airlock
175
+ ```
176
+
177
+ #### 💻 OpenCode (`opencode.json`)
178
+ ```json
179
+ {
180
+ "$schema": "https://opencode.ai/config.json",
181
+ "mcp": {
182
+ "code-airlock": {
183
+ "type": "local",
184
+ "command": ["uvx", "code-airlock"],
185
+ "enabled": true
186
+ }
187
+ }
188
+ }
189
+ ```
190
+
191
+ #### 🖱️ Cursor (`.cursor/mcp.json`)
192
+ ```json
193
+ {
194
+ "mcpServers": {
195
+ "code-airlock": {
196
+ "command": "uvx",
197
+ "args": ["code-airlock"]
198
+ }
199
+ }
200
+ }
201
+ ```
202
+
203
+ #### 🖥️ Claude Desktop (`claude_desktop_config.json`) & Windsurf (`~/.codeium/windsurf/mcp_config.json`)
204
+ ```json
205
+ {
206
+ "mcpServers": {
207
+ "code-airlock": {
208
+ "command": "uvx",
209
+ "args": ["code-airlock"]
210
+ }
211
+ }
212
+ }
213
+ ```
214
+
215
+ *(You can also run directly from source via `uvx --from git+https://github.com/sarthexe/code-airlock code-airlock`)*.
216
+
217
+ For a local editable checkout, use the package entrypoint instead of an
218
+ absolute path:
219
+
220
+ ```json
221
+ {
222
+ "mcpServers": {
223
+ "code-airlock": {
224
+ "command": "python",
225
+ "args": ["-m", "code_airlock.mcp_server"]
226
+ }
227
+ }
228
+ }
229
+ ```
230
+
231
+ `python -m code_airlock` starts the same MCP server.
232
+
233
+ ---
234
+
235
+ ### Option 2: Local Development / Manual Git Clone
236
+
237
+ ```bash
238
+ git clone https://github.com/sarthexe/code-airlock.git
239
+ cd code-airlock
240
+ pip install -e ".[dev,benchmark]"
241
+
242
+ # Test tools locally over stdio
243
+ python -m code_airlock
244
+
245
+ # Or run the explicit MCP entrypoint
246
+ code-airlock-mcp
247
+ ```
248
+
249
+ ### Zero-Friction CLI
250
+
251
+ ```bash
252
+ python -m code_airlock doctor # model warmup + round-trip + MCP config
253
+ python -m code_airlock init # add rules to .cursorrules / CLAUDE.md
254
+ python -m code_airlock init --pre-commit # also install .git/hooks/pre-commit
255
+ python -m code_airlock init --config # create a starter .airlock.toml
256
+ python -m code_airlock scan --staged # fast Stage 1 scan of staged files
257
+ python -m code_airlock scan --deep src/ # add GLiNER detection for a path
258
+ python -m code_airlock proxy # start the HTTP proxy
259
+ python -m code_airlock # start the MCP server (default)
260
+ ```
261
+
262
+ Installed entrypoints: `code-airlock-mcp`, `code-airlock-proxy`, and the
263
+ backward-compatible `code-airlock` alias.
264
+
265
+ `scan` skips binaries, files over 1 MB, `tests/`, and benchmark fixtures. It
266
+ prints `file:line -> [CATEGORY] masked-preview` and exits `1` when secrets are
267
+ found, so it works unchanged in CI and Git hooks. `--deep` enables Stage 2 NER;
268
+ the default Stage 1 path is sub-millisecond. `init` is idempotent, and
269
+ `init --pre-commit` installs a hook that runs `python -m code_airlock scan --staged`.
270
+
271
+ ### 🧰 MCP Tools
272
+
273
+ | Tool | Purpose |
274
+ | :--- | :--- |
275
+ | `airlock_read_file` | Read a bounded UTF-8 file through the vault; blocks `.ssh`, `.aws`, `.gnupg`, `.kube`, `/etc`, `System32`, `AIRLOCK_WORKSPACE` escapes, binaries, and files over 1 MB. |
276
+ | `airlock_write_file` | Rehydrate locally and write; refuses to write if unresolved/hallucinated `<ENTITY_N>` tokens remain. |
277
+ | `airlock_scrub_text` | Redact arbitrary in-memory text or terminal output; `use_ner=False` forces the fast Stage 1 path. |
278
+ | `airlock_exec` | Run a guarded subprocess (`shell=False`), inject real `.env` values into its environment, and scrub stdout/stderr before returning. Blocks destructive executables and shells. |
279
+ | `airlock_vault_status` | Return active token keys and per-category counts only; never raw values. |
280
+ | `airlock_dehydrate` | Redact text and return session-stable tokens. |
281
+ | `airlock_rehydrate` | Restore canonical, case-mangled, or whitespace-padded vault tokens. |
282
+
283
+ All file tools resolve paths with `Path.expanduser().resolve()` and jail access
284
+ to `AIRLOCK_WORKSPACE` when that variable is set. Rehydration is tolerant of
285
+ `<api_key_1>` / `< API_KEY_1 >`, while the SSE `StreamRehydrator` remains
286
+ zero-delay and exact.
287
+
288
+ `airlock_exec` is designed for safe test runs:
289
+
290
+ ```python
291
+ airlock_exec(
292
+ [sys.executable, "-c", "import os; print(os.environ['STRIPE_SECRET_KEY'])"],
293
+ ".env",
294
+ )
295
+ # stdout: "<API_KEY_1>" ← the real key never reaches the model
296
+ ```
297
+
298
+ ### ⚙️ Project Config (`.airlock.toml`)
299
+
300
+ Create a starter with `python -m code_airlock init --config`, or write one
301
+ manually. `CascadeEngine` loads `.airlock.toml` from the current directory, or
302
+ from `AIRLOCK_CONFIG` when set:
303
+
304
+ ```toml
305
+ [allowlist]
306
+ terms = ["MyInternalApp", "staging.corp.local"]
307
+
308
+ [custom_patterns]
309
+ EMPLOYEE_ID = 'EMP-[0-9]{6}'
310
+ INTERNAL_TICKET = 'JIRA-[0-9]{4,6}'
311
+ ```
312
+
313
+ - Allowlist terms are merged into the Stage 2 allowlist so they are not
314
+ misclassified as `PERSON`, `ORGANIZATION`, or `LOCATION`.
315
+ - Custom patterns run in Stage 1 alongside built-in rules and produce
316
+ `<EMPLOYEE_ID_n>` / `<INTERNAL_TICKET_n>` vault tokens.
317
+ - Invalid regexes and malformed labels are ignored rather than crashing the
318
+ engine.
319
+
320
+ ---
321
+
322
+ ### Option 3: OpenAI-Compatible Reverse Proxy (`/v1/chat/completions`)
323
+
324
+ Run as a transparent privacy gateway intercepting HTTP requests:
325
+
326
+ ```bash
327
+ # Run with built-in zero-leak mock upstream (offline validation)
328
+ code-airlock-proxy
329
+
330
+ # Or point to a live upstream provider (OpenAI, Ollama, OpenRouter, vLLM)
331
+ export AIRLOCK_UPSTREAM_URL="https://api.openai.com"
332
+ code-airlock-proxy
333
+ ```
334
+
335
+ The proxy binds to `127.0.0.1:8000` by default. Override with
336
+ `AIRLOCK_HOST` / `AIRLOCK_PORT`.
337
+
338
+ ---
339
+
340
+ ## 🧠 Guiding Your Agent (`.cursorrules` / System Prompt)
341
+
342
+ To ensure your coding agent actually leverages the airlock tools instead of raw file reads, include this rule in your `.cursorrules`, `CLAUDE.md`, or system instructions:
343
+
344
+ ```markdown
345
+ # Code-Airlock Agent Guardrails
346
+ 1. When reading `.env`, configuration, or log files, NEVER use standard read tools. ALWAYS call `airlock_read_file(path)` so secrets and PII are redacted into `<ENTITY_n>` tokens before hitting context.
347
+ 2. When creating or editing configs, write the `<ENTITY_n>` tokens directly and call `airlock_write_file(path, content)`. The airlock will rehydrate the real secrets locally on disk without ever sending them through the LLM.
348
+ 3. Call `airlock_vault_status()` to inspect active pseudonymized tokens safely (never returns plaintext values).
349
+ ```
350
+
351
+ ## 📁 Repository Structure
352
+
353
+ | File | Lines | Responsibility |
354
+ | :--- | :---: | :--- |
355
+ | `src/code_airlock/engine.py` | 219 | Stage 1 regex/checksum validators + high-entropy URI/KV secrets + lazy Stage 2 GLiNER |
356
+ | `src/code_airlock/sanitizer.py` | 108 | Vault hygiene, tolerant rehydration, unresolved-token detection, SSE parser |
357
+ | `src/code_airlock/proxy.py` | 153 | FastAPI `/v1/chat/completions` gateway, SSE rehydration, telemetry headers, `main()` |
358
+ | `src/code_airlock/mcp_server.py` | 124 | FastMCP server with path jailing, file guardrails, `airlock_scrub_text`, `airlock_exec`, and write verification |
359
+ | `src/code_airlock/config.py` | 39 | Optional `.airlock.toml` allowlist and custom Stage-1 pattern loader |
360
+ | `src/code_airlock/executor.py` | 87 | Guarded subprocess execution, env injection, and output scrubbing |
361
+ | `src/code_airlock/scanner.py` | 76 | Masked Stage-1/Stage-2 Git and path secret scanner |
362
+ | `src/code_airlock/__init__.py` | 7 | Public API: `CascadeEngine`, `VaultSession`, `StreamRehydrator`, `__version__` |
363
+ | `src/code_airlock/__main__.py` | 127 | CLI: `mcp`, `proxy`, `doctor`, `init`, `scan` |
364
+ | `tests/test_proxy.py` | 84 | End-to-end proxy tests (11-entity zero-leak check, 2-char SSE split, C++ syntax) |
365
+ | `tests/test_mcp.py` | 181 | MCP tools, scanner, exec, config, pre-commit hook, file jail, tolerant rehydration |
366
+ | `benchmarks/benchmark.py` | 163 | Unified 4-suite evaluator → `benchmarks/BENCHMARK_RESULTS.md` |
367
+ | `benchmarks/build_benchmarks.py` | 239 | Deterministic 2,000-row generator (`seed=42`) |
368
+ | `benchmarks/export_hf.py` | 88 | Publishes all 4 splits plus the auto-generated dataset card |
369
+ | `benchmarks/BENCHMARK_RESULTS.md` | — | Generated metrics-only scorecard |
370
+ | `benchmarks/gold_set.jsonl` | — | 285-row real-world regression corpus |
371
+ | `benchmarks/*.jsonl` | — | Generated 4-split fixtures (git-ignored) |
372
+ | `pyproject.toml` | — | Package metadata, src discovery, entrypoints, extras, pytest config |
373
+ | `LICENSE` / `SECURITY.md` | — | MIT license and disclosure policy |
374
+ | `.github/workflows/` | — | CI test workflow + PyPI Trusted Publishing |
375
+
376
+ ---
377
+
378
+ ## 🔐 Security
379
+
380
+ Detection is best-effort and local-only. It reduces exposure but is not a
381
+ guarantee that every secret is removed. See [SECURITY.md](SECURITY.md) for the
382
+ threat model, known gaps, and private disclosure instructions.
383
+
384
+ The MCP file tools additionally block sensitive directories (`~/.ssh`, `~/.aws`,
385
+ `~/.gnupg`, `~/.kube`, `/etc`, `System32`), files over 1 MB, binary files, and
386
+ paths outside `AIRLOCK_WORKSPACE` when that variable is set. Writes containing
387
+ unresolved or hallucinated `<ENTITY_N>` tokens are refused.
388
+
389
+ `code-airlock scan --staged` adds a Git pre-commit gate for the five highest-risk
390
+ secret categories (`API_KEY`, `AUTH_TOKEN`, `CREDIT_CARD`, `GOVT_ID`,
391
+ `IBAN_CRYPTO`). It prints masked previews only and exits non-zero on findings.
392
+
393
+ ## 📜 License
394
+
395
+ MIT © 2026 Sarthak Maurya. See [LICENSE](LICENSE).